(*
  Copyright (c) 2009 Barry Schwartz

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*)

(*-----------------------------------------------------------------------*)  

open XNum
open Logging
open Dim
open Box
open ParseState
open ParseArgs
open Pycaml
open Python_tools
open UniTools
  

(* string constants *******************************************************************************)

let str_adj_demerits = UString.uc_string_of_ascii "adj-demerits"
let str_alignment = UString.uc_string_of_ascii "alignment"
let str_baseline_skip = UString.uc_string_of_ascii "baseline-skip"
let str_binop_penalty = UString.uc_string_of_ascii "binop-penalty"
let str_bottom_skip = UString.uc_string_of_ascii "bottom-skip"
let str_british = UString.uc_string_of_ascii "british"
let str_club_penalty = UString.uc_string_of_ascii "club-penalty"
let str_delimiter_factor = UString.uc_string_of_ascii "delimiter-factor"
let str_delimiter_shortfall = UString.uc_string_of_ascii "delimiter-shortfall"
let str_direct = UString.uc_string_of_ascii "direct"
let str_double_hyphen_demerits = UString.uc_string_of_ascii "double-hyphen-demerits"
let str_dpi = UString.uc_string_of_ascii "dpi"
let str_emergency_stretch = UString.uc_string_of_ascii "emergency-stretch"
let str_ex_hyphen_penalty = UString.uc_string_of_ascii "ex-hyphen-penalty"
let str_family = UString.uc_string_of_ascii "family"
let str_features = UString.uc_string_of_ascii "features"
let str_final_hyphen_demerits = UString.uc_string_of_ascii "final-hyphen-demerits"
let str_fixed = UString.uc_string_of_ascii "fixed"
let str_float = UString.uc_string_of_ascii "float"
let str_float_sep = UString.uc_string_of_ascii "float-sep"
let str_footnote = UString.uc_string_of_ascii "footnote"
let str_galley = UString.uc_string_of_ascii "galley"
let str_grid_size = UString.uc_string_of_ascii "grid-size"
let str_height = UString.uc_string_of_ascii "height"
let str_hyphen = UString.uc_string_of_ascii "hyphen"
let str_hyphen_params = UString.uc_string_of_ascii "hyphen-params"
let str_hyphen_penalty = UString.uc_string_of_ascii "hyphen-penalty"
let str_hyphen_table = UString.uc_string_of_ascii "hyphen-table"
let str_leading = UString.uc_string_of_ascii "leading"
let str_left = UString.uc_string_of_ascii "left"
let str_left_annotation = UString.uc_string_of_ascii "left-annotation"
let str_left_hyphen_min = UString.uc_string_of_ascii "left-hyphen-min"
let str_left_par_shape = UString.uc_string_of_ascii "left-par-shape"
let str_left_skip = UString.uc_string_of_ascii "left-skip"
let str_line_break_params = UString.uc_string_of_ascii "line-break-params"
let str_line_params = UString.uc_string_of_ascii "line-params"
let str_line_penalty = UString.uc_string_of_ascii "line-penalty"
let str_line_skip = UString.uc_string_of_ascii "line-skip"
let str_line_skip_limit = UString.uc_string_of_ascii "line-skip-limit"
let str_looseness = UString.uc_string_of_ascii "looseness"
let str_math_family = UString.uc_string_of_ascii "math-family"
let str_math_params = UString.uc_string_of_ascii "math-params"
let str_measure = UString.uc_string_of_ascii "measure"
let str_med_math_skip = UString.uc_string_of_ascii "med-math-skip"
let str_min_size = UString.uc_string_of_ascii "min-size"
let str_name = UString.uc_string_of_ascii "name"
let str_no_victorian_spacing = UString.uc_string_of_ascii "no-victorian-spacing"
let str_null_delimiter_space = UString.uc_string_of_ascii "null-delimiter-space"
let str_page = UString.uc_string_of_ascii "page"
let str_par_indent = UString.uc_string_of_ascii "par-indent"
let str_par_fill_skip = UString.uc_string_of_ascii "par-fill-skip"
let str_par_params = UString.uc_string_of_ascii "par-params"
let str_par_skip = UString.uc_string_of_ascii "par-skip"
let str_post_break = UString.uc_string_of_ascii "post-break"
let str_post_process_line = UString.uc_string_of_ascii "post-process-line"
let str_pre_break = UString.uc_string_of_ascii "pre-break"
let str_pre_tolerance = UString.uc_string_of_ascii "pre-tolerance"
let str_register = UString.uc_string_of_ascii "register"
let str_rel_penalty = UString.uc_string_of_ascii "rel-penalty"
let str_right = UString.uc_string_of_ascii "right"
let str_right_annotation = UString.uc_string_of_ascii "right-annotation"
let str_right_hyphen_min = UString.uc_string_of_ascii "right-hyphen-min"
let str_right_par_shape = UString.uc_string_of_ascii "right-par-shape"
let str_right_skip = UString.uc_string_of_ascii "right-skip"
let str_river_demerits = UString.uc_string_of_ascii "river-demerits"
let str_river_threshold = UString.uc_string_of_ascii "river-threshold"
let str_scale = UString.uc_string_of_ascii "scale"
let str_script_lang = UString.uc_string_of_ascii "script-lang"
let str_script_size = UString.uc_string_of_ascii "script-size"
let str_script2_size = UString.uc_string_of_ascii "script-script-size"
let str_separator = UString.uc_string_of_ascii "separator"
let str_series = UString.uc_string_of_ascii "series"
let str_shape = UString.uc_string_of_ascii "shape"
let str_size = UString.uc_string_of_ascii "size"
let str_simple_breaking = UString.uc_string_of_ascii "simple-breaking"
let str_skew = UString.uc_string_of_ascii "skew"
let str_skyline = UString.uc_string_of_ascii "skyline"
let str_space_factor = UString.uc_string_of_ascii "space-factor"
let str_space_params = UString.uc_string_of_ascii "space-params"
let str_space_skip = UString.uc_string_of_ascii "space-skip"
let str_script_space = UString.uc_string_of_ascii "script-space"
let str_table_entry_left = UString.uc_string_of_ascii "table-entry:left"
let str_table_entry_right = UString.uc_string_of_ascii "table-entry:right"
let str_table_entry_top = UString.uc_string_of_ascii "table-entry:top"
let str_table_entry_baseline = UString.uc_string_of_ascii "table-entry:baseline"
let str_table_entry_bottom = UString.uc_string_of_ascii "table-entry:bottom"
let str_TeX = UString.uc_string_of_ascii "TeX"
let str_text_size = UString.uc_string_of_ascii "text-size"
let str_thick_math_skip = UString.uc_string_of_ascii "thick-math-skip"
let str_thin_math_skip = UString.uc_string_of_ascii "thin-math-skip"
let str_tolerance = UString.uc_string_of_ascii "tolerance"
let str_top_skip = UString.uc_string_of_ascii "top-skip"
let str_vert = UString.uc_string_of_ascii "vert"
let str_victorian_spacing = UString.uc_string_of_ascii "victorian-spacing"
let str_widow_penalty = UString.uc_string_of_ascii "widow-penalty"
let str_width = UString.uc_string_of_ascii "width"
let str_xspace_skip = UString.uc_string_of_ascii "xspace-skip"
  

(* parameter dictionaries *************************************************************************)

let lookup_in_dict dict name conv default =
  match DynUCTrie.lookup_string name dict with
  | Some (Some d) -> Some (conv d)
  | Some None -> default
  | None -> None
  
let lookup_list dict name = lookup_in_dict dict name (fun x -> x) None
let lookup_string dict name = lookup_in_dict dict name Array.of_list None
let lookup_int dict name = lookup_in_dict dict name Parser.str_to_int None
let lookup_num dict name = lookup_in_dict dict name Parser.str_to_num None
let lookup_skip dict name = lookup_in_dict dict name Parser.str_expr_to_skip None
let lookup_dim dict name = lookup_in_dict dict name Parser.str_expr_to_dim None
let lookup_bool dict name = lookup_in_dict dict name Parser.str_to_bool (Some true)
  
(*
  |check_keys <dict> <key-dict> <loc>| checks whether every key of <dict> appears in <key-dict>
  and prints out a warning otherwise.
*)
let check_keys dict key_dict loc =
  DynUCTrie.iter
    (fun k _ ->
       if not (DynUCTrie.mem_string k key_dict) then
         begin
           log_warn loc "unknown key `";
           log_uc_string k;
           log_string "'!"
         end)
    dict
  
(* |leading_dict| maps the name of a leading method to the corresponding function. *)
let leading_dict =
  DynUCTrie.add_string str_fixed Galley.leading_fixed
    (DynUCTrie.add_string str_register Galley.leading_register
       (DynUCTrie.add_string str_TeX Galley.leading_TeX
          (DynUCTrie.add_string str_skyline Galley.leading_skyline DynUCTrie.
             empty)))
  
(* Supported keys for line-params *)
let line_param_dict =
  DynUCTrie.add_string str_baseline_skip ()
    (DynUCTrie.add_string str_line_skip_limit ()
       (DynUCTrie.add_string str_line_skip ()
          (DynUCTrie.add_string str_leading ()
             (DynUCTrie.add_string str_club_penalty ()
                (DynUCTrie.add_string str_widow_penalty () DynUCTrie.empty)))))
  
let parse_line_param loc dict =
  (check_keys dict line_param_dict loc;
   let baseline_skip = lookup_dim dict str_baseline_skip in
   let line_skip_limit = lookup_skip dict str_line_skip_limit in
   let line_skip = lookup_dim dict str_line_skip in
   let leading_type = lookup_string dict str_leading in
   let club_penalty = lookup_num dict str_club_penalty in
   let widow_penalty = lookup_num dict str_widow_penalty in
   let leading =
     match leading_type with
     | None -> None
     | Some str ->
         (match DynUCTrie.lookup_string str leading_dict with
          | Some l -> Some l
          | None ->
              (log_warn loc "unknown leading `";
               log_uc_string str;
               log_string "`.";
               Some Galley.leading_TeX)) in
   let simple_cw club_penalty widow_penalty above below =
     if above <= 1
     then club_penalty
     else if below <= 1 then widow_penalty else num_zero in
   let club_widow_penalty =
     match (club_penalty, widow_penalty) with
     | (None, None) -> None
     | (Some c, Some w) -> Some (simple_cw c w)
     | (Some c, None) -> Some (simple_cw c num_zero)
     | (None, Some w) -> Some (simple_cw num_zero w)
   in
     (baseline_skip, line_skip_limit, line_skip, leading, club_widow_penalty))
  
let parse_par_shape left_stream right_stream =
  let read_next stream =
    let (a, b) = Parser.read_range stream
    in
      if (UCStream.next_char stream) = 58
      then
        ((* : *)
         UCStream.remove stream 1;
         Parser.skip_spaces stream;
         ((int_of_num (ceiling_num a)), (int_of_num (floor_num b)),
          (Parser.read_skip stream)))
      else
        (log_warn (UCStream.location stream) "Invalid par shape!";
         log_int (UCStream.next_char stream);
         ((-1), (-1), (fun _ -> num_zero))) in
  let rec parse_shape stream =
    (Parser.skip_spaces stream;
     if (UCStream.next_char stream) >= 0
     then
       (let (a, b, s) = read_next stream
        in
          if a < 0
          then []
          else
            (Parser.skip_spaces stream;
             (match UCStream.next_char stream with
              | 44 | 59 ->
                  (UCStream.remove stream 1;
                   (a, b, s) :: (parse_shape stream))
              | (-1) -> [ (a, b, s) ]
              | _ ->
                  (log_warn (UCStream.location stream) "Invalid par shape!";
                   [ (a, b, s) ]))))
     else []) in
  let left_indent = parse_shape left_stream in
  let right_indent = parse_shape right_stream in
  let rec lookup n list =
    match list with
    | [] -> (fun _ -> num_zero)
    | (a, b, s) :: ls -> if (a <= n) && (n <= b) then s else lookup n ls in
  let par_shape env n =
    ((lookup n left_indent env), (lookup n right_indent env))
  in par_shape
  
let parse_boxes ps code env =
  let nodes = execute_string_in_mode ps code `HBox in
  let (b, get) =
    Compose.hyph_only_builder (Environment.current_font_metric env)
      (Environment.current_composer env)
      (Galley.hyphen_params (Environment.current_galley env)) in
  let _ = Evaluate.eval_node_list env b nodes in get ()
  
let parse_annotation ps left_annotation right_annotation =
  let new_ps = duplicate ps in
  let annotate env boxes =
    (* <left> and <right> should evaluate to a box of width zero. *)
    let left = execute_string_in_mode new_ps left_annotation `HBox in
    let right = execute_string_in_mode new_ps right_annotation `HBox in
    let (b, get) =
      Builder.simple_builder (Environment.current_font_metric env)
        (Environment.current_composer env) in
    let _ = Evaluate.eval_node_list env b left
    in
      (Builder.add_box_list b boxes;
       let _ = Evaluate.eval_node_list env b right in get ())
  in annotate
  
let par_param_dict =
  DynUCTrie.add_string str_measure ()
    (DynUCTrie.add_string str_par_indent ()
       (DynUCTrie.add_string str_par_fill_skip ()
          (DynUCTrie.add_string str_left_skip ()
             (DynUCTrie.add_string str_right_skip ()
                (DynUCTrie.add_string str_left_par_shape ()
                   (DynUCTrie.add_string str_right_par_shape ()
                      (DynUCTrie.add_string str_par_skip ()
                         (DynUCTrie.add_string str_left_annotation ()
                            (DynUCTrie.add_string str_right_annotation ()
                               (DynUCTrie.add_string str_pre_break ()
                                  (DynUCTrie.add_string str_post_break ()
                                     (DynUCTrie.add_string
                                        str_post_process_line () DynUCTrie.empty))))))))))))
  
let parse_par_param ps loc dict =
  (check_keys dict par_param_dict loc;
   let measure = lookup_num dict str_measure in
   let par_indent = lookup_dim dict str_par_indent in
   let par_fill_skip = lookup_dim dict str_par_fill_skip in
   let left_skip = lookup_dim dict str_left_skip in
   let right_skip = lookup_dim dict str_right_skip in
   let par_shape_left = lookup_string dict str_left_par_shape in
   let par_shape_right = lookup_string dict str_right_par_shape in
   let par_skip = lookup_dim dict str_par_skip in
   let left_ann = lookup_list dict str_left_annotation in
   let right_ann = lookup_list dict str_right_annotation in
   let pre_break = lookup_list dict str_pre_break in
   let post_break = lookup_list dict str_post_break in
   let post_process_line = None in (* FIX *)
   let par_shape =
     match (par_shape_left, par_shape_right) with
     | (None, None) -> None
     | (Some left, None) ->
         Some
           (parse_par_shape (UCStream.of_string left) (UCStream.of_list []))
     | (None, Some right) ->
         Some
           (parse_par_shape (UCStream.of_list []) (UCStream.of_string right))
     | (Some left, Some right) ->
         Some
           (parse_par_shape (UCStream.of_string left)
              (UCStream.of_string right)) in
   let post_process =
     match (post_process_line, left_ann, right_ann) with
     | (Some p, _, _) -> Some p
     | (None, Some l, Some r) -> Some (parse_annotation ps l r)
     | (None, Some l, None) -> Some (parse_annotation ps l [])
     | (None, None, Some r) -> Some (parse_annotation ps [] r)
     | (None, None, None) -> None in
   let pre_break2 =
     match pre_break with | Some p -> Some (parse_boxes ps p) | None -> None in
   let post_break2 =
     match post_break with | Some p -> Some (parse_boxes ps p) | None -> None
   in
     (measure, par_indent, par_fill_skip, left_skip, right_skip, par_shape,
      par_skip, pre_break2, post_break2, post_process))
  
let line_break_param_dict =
  DynUCTrie.add_string str_pre_tolerance ()
    (DynUCTrie.add_string str_tolerance ()
       (DynUCTrie.add_string str_looseness ()
          (DynUCTrie.add_string str_line_penalty ()
             (DynUCTrie.add_string str_adj_demerits ()
                (DynUCTrie.add_string str_double_hyphen_demerits ()
                   (DynUCTrie.add_string str_final_hyphen_demerits ()
                      (DynUCTrie.add_string str_emergency_stretch ()
                         (DynUCTrie.add_string str_simple_breaking ()
                            (DynUCTrie.add_string str_river_demerits ()
                               (DynUCTrie.add_string str_river_threshold ()
                                  DynUCTrie.empty))))))))))
  
let parse_line_break_param loc dict =
  (check_keys dict line_break_param_dict loc;
   let pre_tolerance = lookup_num dict str_pre_tolerance in
   let tolerance = lookup_num dict str_tolerance in
   let looseness = lookup_int dict str_looseness in
   let line_penalty = lookup_num dict str_line_penalty in
   let adj_demerits = lookup_num dict str_adj_demerits in
   let double_hyphen_demerits = lookup_num dict str_double_hyphen_demerits in
   let final_hyphen_demerits = lookup_num dict str_final_hyphen_demerits in
   let emergency_stretch = lookup_skip dict str_emergency_stretch in
   let river_demerits = lookup_num dict str_river_demerits in
   let river_threshold = lookup_skip dict str_river_threshold in
   let simple_breaking = lookup_bool dict str_simple_breaking
   in
     (pre_tolerance, tolerance, looseness, line_penalty, adj_demerits,
      double_hyphen_demerits, final_hyphen_demerits, emergency_stretch,
      river_demerits, river_threshold, simple_breaking))
  
let hyphen_param_dict =
  DynUCTrie.add_string str_hyphen_table ()
    (DynUCTrie.add_string str_hyphen_penalty ()
       (DynUCTrie.add_string str_ex_hyphen_penalty ()
          (DynUCTrie.add_string str_left_hyphen_min ()
             (DynUCTrie.add_string str_right_hyphen_min ()
                (DynUCTrie.add_string str_script_lang () DynUCTrie.empty)))))
  
let parse_hyphen_param loc dict =
  (check_keys dict hyphen_param_dict loc;
   let hyphen_table = lookup_string dict str_hyphen_table in
   let hyphen_penalty = lookup_num dict str_hyphen_penalty in
   let ex_hyphen_penalty = lookup_num dict str_ex_hyphen_penalty in
   let left_hyphen_min = lookup_int dict str_left_hyphen_min in
   let right_hyphen_min = lookup_int dict str_right_hyphen_min in
   let script_lang = lookup_string dict str_script_lang
   in
     (hyphen_table, hyphen_penalty, ex_hyphen_penalty, left_hyphen_min,
      right_hyphen_min, script_lang))
  
let font_dict =
  DynUCTrie.add_string str_family ()
    (DynUCTrie.add_string str_series ()
       (DynUCTrie.add_string str_shape ()
          (DynUCTrie.add_string str_size ()
             (DynUCTrie.add_string str_script_lang ()
                (DynUCTrie.add_string str_features () DynUCTrie.empty)))))
  
let parse_font_dict loc dict =
  (check_keys dict font_dict loc;
   let family = lookup_string dict str_family in
   let series = lookup_string dict str_series in
   let shape = lookup_string dict str_shape in
   let size = lookup_num dict str_size in
   let script = lookup_string dict str_script_lang in
   let features =
     match lookup_list dict str_features with
     | None -> None
     | Some str -> Some (List.map Array.of_list (Parser.str_to_list str))
   in (family, series, shape, size, script, features))
  
let math_font_dict =
  DynUCTrie.add_string str_math_family ()
    (DynUCTrie.add_string str_family ()
       (DynUCTrie.add_string str_series ()
          (DynUCTrie.add_string str_shape ()
             (DynUCTrie.add_string str_text_size ()
                (DynUCTrie.add_string str_script_size ()
                   (DynUCTrie.add_string str_script2_size () DynUCTrie.empty))))))
  
let parse_math_font_dict loc dict =
  (check_keys dict math_font_dict loc;
   let math_family = lookup_int dict str_math_family in
   let family = lookup_string dict str_family in
   let series = lookup_string dict str_series in
   let shape = lookup_string dict str_shape in
   let text_size = lookup_num dict str_text_size in
   let script_size = lookup_num dict str_script_size in
   let script2_size = lookup_num dict str_script2_size
   in
     (math_family, family, series, shape, text_size, script_size,
      script2_size))
  
let space_param_dict =
  DynUCTrie.add_string str_space_factor ()
    (DynUCTrie.add_string str_space_skip ()
       (DynUCTrie.add_string str_xspace_skip ()
          (DynUCTrie.add_string str_victorian_spacing ()
             (DynUCTrie.add_string str_no_victorian_spacing () DynUCTrie.
                empty))))
  
let parse_space_param loc dict =
  (check_keys dict space_param_dict loc;
   let space_factor = lookup_num dict str_space_factor in
   let space_skip = lookup_dim dict str_space_skip in
   let xspace_skip = lookup_dim dict str_xspace_skip in
   let victorian = lookup_bool dict str_victorian_spacing in
   let novictorian = lookup_bool dict str_no_victorian_spacing in
   let vic = match victorian with | Some _ -> victorian | None -> novictorian
   in (space_factor, space_skip, xspace_skip, vic))
  
let math_param_dict =
  DynUCTrie.add_string str_thin_math_skip ()
    (DynUCTrie.add_string str_med_math_skip ()
       (DynUCTrie.add_string str_thick_math_skip ()
          (DynUCTrie.add_string str_script_space ()
             (DynUCTrie.add_string str_rel_penalty ()
                (DynUCTrie.add_string str_binop_penalty ()
                   (DynUCTrie.add_string str_delimiter_factor ()
                      (DynUCTrie.add_string str_delimiter_shortfall ()
                         (DynUCTrie.add_string str_null_delimiter_space ()
                            DynUCTrie.empty))))))))
  
let parse_math_param loc dict =
  (check_keys dict math_param_dict loc;
   let thin_math_skip = lookup_dim dict str_thin_math_skip in
   let med_math_skip = lookup_dim dict str_med_math_skip in
   let thick_math_skip = lookup_dim dict str_thick_math_skip in
   let script_space = lookup_dim dict str_script_space in
   let rel_penalty = lookup_num dict str_rel_penalty in
   let binop_penalty = lookup_num dict str_binop_penalty in
   let delimiter_factor = lookup_num dict str_delimiter_factor in
   let delimiter_shortfall = lookup_skip dict str_delimiter_shortfall in
   let null_delimiter_space = lookup_dim dict str_null_delimiter_space
   in
     (thin_math_skip, med_math_skip, thick_math_skip, script_space,
      rel_penalty, binop_penalty, delimiter_factor, delimiter_shortfall,
      null_delimiter_space))
  
let galley_area_dict =
  DynUCTrie.add_string str_name ()
    (DynUCTrie.add_string str_top_skip ()
       (DynUCTrie.add_string str_bottom_skip ()
          (DynUCTrie.add_string str_min_size ()
             (DynUCTrie.add_string str_grid_size () DynUCTrie.empty))))
  
let parse_galley_area_dict loc dict =
  (check_keys dict galley_area_dict loc;
   let name = lookup_string dict str_name in
   let top = lookup_skip dict str_top_skip in
   let bot = lookup_skip dict str_bottom_skip in
   let min = lookup_skip dict str_min_size in
   let grid = lookup_skip dict str_grid_size
   in
     ((Option.from_option [|  |] name),
      (Option.from_option (Evaluate.const_em num_one) top),
      (Option.from_option (Evaluate.const_em num_one) bot),
      (Option.from_option (Evaluate.const_em (num_of_int 5)) min),
      (Option.from_option (Evaluate.const_em num_one) grid)))
  
let float_area_dict =
  DynUCTrie.add_string str_alignment ()
    (DynUCTrie.add_string str_top_skip ()
       (DynUCTrie.add_string str_bottom_skip ()
          (DynUCTrie.add_string str_float_sep () DynUCTrie.empty)))
  
let parse_float_area_dict loc dict =
  (check_keys dict float_area_dict loc;
   let align =
     match DynUCTrie.lookup_string str_alignment dict with
       | Some (Some str) ->
           (match UString.to_string str with
              | "top" -> FloatVertical.Top
              | "bottom" -> FloatVertical.Bottom
              | _ ->
                  (log_warn loc "unknown alignment `";
                   log_uc_list str;
                   log_string "'!";
                   FloatVertical.
                     Top))
       | _ -> FloatVertical.Top in
   let top = lookup_skip dict str_top_skip in
   let bot = lookup_skip dict str_bottom_skip in
   let sep = lookup_dim dict str_float_sep
   in
     (align, (Option.from_option (Evaluate.const_em num_one) top),
      (Option.from_option (Evaluate.const_em num_one) bot),
      (Option.from_option
         (Evaluate.const_fixed_dim (Evaluate.const_em num_one)) sep)))
  
let footnote_area_dict =
  DynUCTrie.add_string str_separator ()
    (DynUCTrie.add_string str_top_skip ()
       (DynUCTrie.add_string str_bottom_skip ()
          (DynUCTrie.add_string str_grid_size ()
             (DynUCTrie.add_string str_line_params ()
                (DynUCTrie.add_string str_par_params ()
                   (DynUCTrie.add_string str_line_break_params ()
                      (DynUCTrie.add_string str_hyphen_params ()
                         (DynUCTrie.add_string str_space_params ()
                            (DynUCTrie.add_string str_math_params ()
                               DynUCTrie.empty)))))))))
  
let parse_footnote_area_dict ps loc dict =
  let lookup_key_val dict name =
    match lookup_list dict name with
      | None -> DynUCTrie.empty
      | Some str -> Parser.str_to_key_val str
  in
    (check_keys dict footnote_area_dict loc;
     let separator =
       match lookup_list dict str_separator with
         | None -> []
         | Some str ->
             let new_ps = duplicate ps
             in execute_string_in_mode new_ps str `VBox in
     let top = lookup_skip dict str_top_skip in
     let bot = lookup_skip dict str_bottom_skip in
     let grid = lookup_skip dict str_grid_size in
     let line_params = lookup_key_val dict str_line_params in
     let par_params = lookup_key_val dict str_par_params in
     let line_break_params = lookup_key_val dict str_line_break_params in
     let hyphen_params = lookup_key_val dict str_hyphen_params in
     let space_params = lookup_key_val dict str_space_params in
     let math_params = lookup_key_val dict str_math_params
     in
       (separator, (Option.from_option (Evaluate.const_em num_one) top),
        (Option.from_option (Evaluate.const_em num_one) bot),
        (Option.from_option (Evaluate.const_em num_one) grid),
        (parse_line_param loc line_params),
        (parse_par_param ps loc par_params),
        (parse_line_break_param loc line_break_params),
        (parse_hyphen_param loc hyphen_params),
        (parse_space_param loc space_params),
        (parse_math_param loc math_params)))
  

(* primitive commands *****************************************************************************)

(*-----------------------------------------------------------------------*)
(* parameters *)

let set_param ps =
  let param = arg_expanded ps in
  let loc = location ps in
  let contents = arg_key_val ps in
  let add_cmd cmd parse =
    add_node ps (Node.Command (loc, cmd (parse loc contents)))
  in
    match UString.to_string param with
      | "font" -> add_cmd Environment.set_font parse_font_dict
      | "paragraph" -> add_cmd Environment.set_par_params (parse_par_param ps)
      | "line" -> add_cmd Environment.set_line_params parse_line_param
      | "line-break" ->
          add_cmd Environment.set_line_break_params parse_line_break_param
      | "hyphenation" ->
          add_cmd Environment.set_hyphen_params parse_hyphen_param
      | "space" -> add_cmd Environment.set_space_params parse_space_param
      | "math" -> add_cmd Environment.set_math_params parse_math_param
      | "this-paragraph" ->
          add_cmd Environment.set_current_par_params (parse_par_param ps)
      | "this-line" ->
          add_cmd Environment.set_current_line_params parse_line_param
      | "this-line-break" ->
          add_cmd Environment.set_current_line_break_params
            parse_line_break_param
      | "this-hyphenation" ->
          add_cmd Environment.set_current_hyphen_params parse_hyphen_param
      | "this-space" ->
          add_cmd Environment.set_current_space_params parse_space_param
      | "this-math" ->
          add_cmd Environment.set_current_math_params parse_math_param
      | _ ->
          log_warn loc ("Unknown dictionary parameter " ^ ((UString.to_string param) ^ "!"))
  
let set_math_font ps =
  let font = parse_math_font_dict (location ps) (arg_key_val ps) in
    add_node ps (Node.Command (location ps, Environment.set_math_font font))
  
let set_mark ps =
  let mark = Array.of_list (arg_expanded ps) in
  let contents = Array.of_list (arg_expanded ps) in
    add_node ps (Node.CommandBox (location ps, `PageCmd (Box.SetMark (mark, contents))))
  

(*-----------------------------------------------------------------------*)
(* boxes and glue *)

let hskip ps =
  begin
    Mode.ensure_par_mode ps;
    let width = arg_TeX_dim ps in
      add_node ps (Node.Glue (location ps, width, (fun _ -> dim_zero), false, true))
  end
  
let vskip ps =
  begin
    Mode.leave_par_mode ps;
    let height = arg_TeX_dim ps in
      add_node ps (Node.Glue (location ps, (fun _ -> dim_zero), height, false, true))
  end
  
let kern ps =
  let skip = arg_TeX_dim ps in
    match current_mode ps with
    | `Galley | `VBox ->
        add_node ps (Node.Glue (location ps, (fun _ -> dim_zero), skip, true, true))
    | `Paragraph | `HBox | `LRBox | `RLBox | `Math ->
        add_node ps (Node.Glue (location ps, skip, (fun _ -> dim_zero), true, true))
    | _ -> ()
  
let hbox dir ps =
  let close_hbox contents = add_node ps (Node.HBox (location ps, dir, contents)) in
  let close_hbox_to width contents = add_node ps (Node.HBoxTo (location ps, dir, width, contents)) in
  let close_hbox_spread amount contents = add_node ps (Node.HBoxSpread (location ps, dir, amount, contents)) in
  let mode = match dir with | `LR -> `LRBox | `RL -> `RLBox | `Default -> `HBox in
  let open_hbox close_command =
    if (CharCode.cat_code (UCStream.next_char ps.input_stream)) = CharCode.BeginGroup then (* Mode.ensure_par_mode ps; *)
      close_command (arg_execute ps mode)
    else
      log_warn (location ps) "syntax error in \\hbox!"
  in
    Parser.skip_blanks ps.input_stream;
    if Parser.match_substring ps.input_stream [ 116; 111 ] 0 then (* to *)
      begin
        UCStream.remove ps.input_stream 2;
        Parser.skip_spaces ps.input_stream;
        let width = Parser.read_simple_skip_expression ps.input_stream in
          open_hbox (fun c -> close_hbox_to width c)
      end
    else
      if Parser.match_substring ps.input_stream [ 115; 112; 114; 101; 97; 100 ] 0 then (* spread *)
        begin
          UCStream.remove ps.input_stream 6;
          Parser.skip_spaces ps.input_stream;
          let amount = Parser.read_simple_skip_expression ps.input_stream in
            open_hbox (fun c -> close_hbox_spread amount c)
        end
      else
        open_hbox close_hbox
  
let vbox ps =
  let close_vbox contents = add_node ps (Node.VBox (location ps, contents)) in
  let close_vbox_to height contents = add_node ps (Node.VBoxTo (location ps, height, contents)) in
  let close_vbox_spread amount contents = add_node ps (Node.VBoxSpread (location ps, amount, contents)) in
  let open_vbox close_command =
    if (CharCode.cat_code (UCStream.next_char ps.input_stream)) = CharCode.BeginGroup then
      close_command (arg_execute ps `VBox)
    else
      log_warn (location ps) "syntax error in \\vbox!"
  in
    Parser.skip_blanks ps.input_stream;
    if Parser.match_substring ps.input_stream [ 116; 111 ] 0 then
      begin
        UCStream.remove ps.input_stream 2;
        Parser.skip_spaces ps.input_stream;
        let height = Parser.read_simple_skip_expression ps.input_stream in
          open_vbox (fun c -> close_vbox_to height c)
      end
    else
      if Parser.match_substring ps.input_stream [ 115; 112; 114; 101; 97; 100 ] 0 then
        begin
          UCStream.remove ps.input_stream 6;
          Parser.skip_spaces ps.input_stream;
          let amount = Parser.read_simple_skip_expression ps.input_stream in
            open_vbox (fun c -> close_vbox_spread amount c)
        end
      else
        open_vbox close_vbox
  
let phantom ps =
  let arg = arg_execute ps `HBox in
    add_node ps (Node.Phantom (location ps, true, true, arg))
  
let hphantom ps =
  let arg = arg_execute ps `HBox in
    add_node ps (Node.Phantom (location ps, true, false, arg))
  
let vphantom ps =
  let arg = arg_execute ps `HBox in
    add_node ps (Node.Phantom (location ps, false, true, arg))
  
let hleaders ps =
  let width = arg_dim ps in
  let body = arg_execute ps `HBox
  in add_node ps (Node.HLeaders (location ps, width, body))
  
let vinsert ps =
  let above = Parser.read_bool ps.input_stream in
  let arg = arg_execute ps `VBox in
    add_node ps (Node.VInsert (location ps, not above, arg))
  
let rule ps =
  let width = arg_dim ps in
  let height = arg_dim ps in
  let depth = arg_dim ps in
    add_node ps (Node.Rule (location ps, width, height, depth))
  
let image ps =
  let add_image file width height =
    add_node ps (Node.Image (location ps, UString.to_string file, width, height))
  in
  let options = opt_key_val ps in
  let file = arg_expanded ps in
    try
      match DynUCTrie.lookup_string str_width options with
        | Some (Some w) ->
            let width = Parser.str_expr_to_skip w in
            let height =
              match DynUCTrie.lookup_string str_height options with
                | Some (Some h) -> Parser.str_expr_to_skip h
                | _ -> failwith "need height"
            in
              add_image file width height
        | _ ->
            begin
              match DynUCTrie.lookup_string str_height options with
                | Some (Some h) ->
                    let height = Parser.str_expr_to_skip h in
                    let width =
                      match DynUCTrie.lookup_string str_width options with
                        | Some (Some w) -> Parser.str_expr_to_skip w
                        | _ -> failwith "need width"
                    in
                      add_image file width height
                | _ ->
                    failwith "need width and height"
            end
    with
      | _ ->
          log_warn (location ps) "Cannot load image `";
          log_uc_list file;
          log_string "'!"

let parbox ps =
  let pos = opt_expanded ps [ 116 ] in
  let width = arg_skip ps in
    (open_node_list ps `Galley;
     execute_argument ps;
     Mode.leave_par_mode ps;
     let body = close_node_list ps `Galley in
     let name = Array.of_list (gen_unique_name ())
     in
       (add_node ps (Node.BeginGroup (location ps));
        add_node ps (Node.NewGalley (location ps, name, width));
        add_node ps (Node.AddToGalley (location ps, name, body));
        (match pos with
         | [ 98 ] ->
             add_node ps (Node.PutGalleyInVBox (location ps, false, name))
         | (*  | [ 99] -> add_node ps (Node.PutGalleyInVBox (location ps) name); *)
             [ 116 ] ->
             add_node ps (Node.PutGalleyInVBox (location ps, true, name))
         | _ ->
             (log_warn (location ps) "pos `";
              log_uc_list pos;
              log_string "' unsupported, assuming `t'!";
              add_node ps (Node.PutGalleyInVBox (location ps, true, name))));
        add_node ps (Node.EndGroup (location ps))))
  
let accent ps =
  let acc = arg_int ps in
  let chr = arg_execute ps `HBox
  in add_node ps (Node.Accent (location ps, acc, chr))
  
(* tables *)
let begin_table_entry ps =
  if (current_mode ps) <> `Table
  then log_warn (location ps) "ignoring table entry outside table!"
  else open_node_list ps `HBox
  
let end_table_entry ps =
  let contents = close_node_list ps `HBox in
  let l = get_counter ps str_table_entry_left in
  let r = get_counter ps str_table_entry_right in
  let t = get_counter ps str_table_entry_top in
  let bl = get_counter ps str_table_entry_baseline in
  let b = get_counter ps str_table_entry_bottom
  in
    (add_node ps (Node.TableEntry (location ps, l, r, t, bl, b, contents));
     (* set size of the next entry *)
     set_counter ps str_table_entry_left (r + 1);
     set_counter ps str_table_entry_right (r + 1);
     set_counter ps str_table_entry_top bl;
     set_counter ps str_table_entry_baseline bl;
     set_counter ps str_table_entry_bottom bl)
  
let new_table_entry ps = (end_table_entry ps; begin_table_entry ps)
  
let new_table_row ps =
  (end_table_entry ps;
   let bl = get_counter ps str_table_entry_baseline
   in
     (set_counter ps str_table_entry_left 0;
      set_counter ps str_table_entry_right 0;
      set_counter ps str_table_entry_top (bl + 1);
      set_counter ps str_table_entry_baseline (bl + 1);
      set_counter ps str_table_entry_bottom (bl + 1);
      begin_table_entry ps))
  
let begin_table ps =
  (open_node_list ps `Table;
   set_counter ps str_table_entry_left 0;
   set_counter ps str_table_entry_right 0;
   set_counter ps str_table_entry_top 0;
   set_counter ps str_table_entry_baseline 0;
   set_counter ps str_table_entry_bottom 0;
   begin_table_entry ps)
  
let end_table ps =
  (end_table_entry ps;
   add_node ps (Node.Table (location ps, close_node_list ps `Table)))
  
(* breaks *)
let penalty ps =
  let arg = arg_num ps
  in add_node ps (Node.Break (location ps, Some arg, false, [], [], []))
  
let discretionary ps =
  let hyph = Parser.read_bool ps.input_stream in
  let penalty =
    match opt_expanded ps [] with
    | [] -> None
    | p -> Some (Parser.str_expr_to_num p) in
  let pre = arg_execute ps `HBox in
  let post = arg_execute ps `HBox in
  let no = arg_execute ps `HBox
  in add_node ps (Node.Break (location ps, penalty, hyph, pre, post, no))
  
(* commands and environments *)
let define_command ps =
  let name = Parser.read_argument ps.input_stream in
  let tmpl = opt_expanded ps [] in
  let args = Macro.parse_arg_template (location ps) tmpl in
  let body = Parser.read_argument ps.input_stream in
    if (Parser.is_token name) && (List.hd name) = CharCode.escape_char then
      ParseState.define_command ps name
        { execute = Macro.execute_macro args body;
          expand = Macro.expand_macro args body }
    else
      begin
        log_warn (location ps) "";
        log_uc_list name;
        log_string " is not a valid command sequence!"
      end
  
let define_pattern ps =
  let name = Parser.read_argument ps.input_stream in
  let tmpl = opt_expanded ps [] in
  let args = Macro.parse_arg_template (location ps) tmpl in
  let body = Parser.read_argument ps.input_stream in
    ParseState.define_pattern ps name
      { execute = Macro.execute_macro args body;
        expand = Macro.expand_macro args body }
  
let save_command ps =
  let name = Parser.read_argument ps.input_stream in
    ParseState.save_command ps name
  
let restore_command ps =
  let name = Parser.read_argument ps.input_stream in
    ParseState.restore_command ps name
  
let save_pattern ps =
  let name = Parser.read_argument ps.input_stream in
    ParseState.save_pattern ps name
  
let restore_pattern ps =
  let name = Parser.read_argument ps.input_stream in
    ParseState.restore_pattern ps name
  
let define_environment ps =
  let name = Parser.read_argument ps.input_stream in
  let tmpl = opt_expanded ps [] in
  let args = Macro.parse_arg_template (location ps) tmpl in
  let begin_body = Parser.read_argument ps.input_stream in
  let end_body = Parser.read_argument ps.input_stream in
    define_env ps name
      { execute = Macro.execute_begin_environment name args begin_body;
        expand = Macro.expand_begin_environment name args begin_body }
      { execute = Macro.execute_end_environment end_body;
        expand = Macro.expand_end_environment end_body }
  
(* |begin_environment| and |end_environment| start and end an environment. *)
let begin_environment ps =
  Macro.begin_env ps (Parser.read_argument ps.input_stream)
  
let end_environment ps =
  Macro.end_env ps (Parser.read_argument ps.input_stream)
  
(* math *)
(* |toggle_math| toggles between math- and text-mode. This corresponds to |$| in \TeX. *)
let toggle_math ps =
  match current_mode ps with
    | `Math -> ignore (Mode.set_mode ps `Paragraph)
    | _ -> ignore (Mode.set_mode ps `Math)
  
let begin_text ps =
  if (current_mode ps) = `Math then
    begin
      open_node_list ps `HBox;
      add_node ps (Node.Command (location ps, Environment.adapt_fonts_to_math_style))
    end
  else
    open_node_list ps `HBox
  
let end_text ps =
  let nodes = close_node_list ps `HBox in
    match current_mode ps with
      | `Preamble | `Galley | `Paragraph ->
          (Mode.ensure_par_mode ps; List.iter (add_node ps) nodes)
      | `HBox | `Table -> List.iter (add_node ps) nodes
      | `Math | `LRBox | `RLBox | `VBox ->
          add_node ps (Node.HBox (location ps, `Default, nodes))
  
(* Print a single math character. *)
let add_math_char ps math_code =
  add_node ps (Node.MathChar (location ps, math_code))
  
(* commands for sub- and super-scripts. *)
let sub_script ps =
  if (current_mode ps) <> `Math then
    log_warn (location ps) "subscript in text mode!"
  else
    match arg_execute ps `Math with
      | [] -> log_warn (location ps) "empty subscript!"
      | nodes -> add_node ps (Node.SubScript (location ps, nodes))
  
let super_script ps =
  if (current_mode ps) <> `Math then
    log_warn (location ps) "superscript in text mode!"
  else
    match arg_execute ps `Math with
      | [] -> log_warn (location ps) "empty superscript!"
      | nodes -> add_node ps (Node.SuperScript (location ps, nodes))
  
let prime ps =
  if (current_mode ps) <> `Math then
    log_warn (location ps) "prime in text mode!"
  else
    add_node ps (Node.SuperScript (location ps, [ Node.MathChar (location ps, (Ordinary, (2, 2), (48, 48))) ]))
  
let overline ps =
  add_node ps (Node.Overline (location ps, arg_execute ps `Math))
  
let underline ps =
  add_node ps (Node.Underline (location ps, arg_execute ps `Math))
  
let math_accent ps family char =
  add_node ps (Node.MathAccent (location ps, family, char, arg_execute ps `Math))
  
let put_root ps small_family small_char large_family large_char =
  add_node ps (Node.Root (location ps, small_family, small_char, large_family,
                          large_char, arg_execute ps `Math))
  
let set_math_code ps code =
  add_node ps (Node.MathCode (location ps, code, arg_execute ps `Math))
  
let left_delim ps =
  open_node_list ps `Math;
  if (UCStream.next_char ps.input_stream) = 46 then
    begin
      UCStream.remove ps.input_stream 1;
      add_node ps (Node.Nodes [ Node.MathChar (location ps, (Open, ((-1), (-1)), ((-1), (-1)))) ])
    end
  else
    add_node ps (Node.Nodes (arg_execute ps `Math));
  open_node_list ps `Math
  
let mid_delim ps =
  (add_node ps (Node.Nodes (close_node_list ps `Math));
   if (UCStream.next_char ps.input_stream) = 46
   then
     (UCStream.remove ps.input_stream 1;
      add_node ps
        (Node.Nodes
           [ Node.MathChar (location ps, (Open, ((-1), (-1)), ((-1), (-1)))) ]))
   else add_node ps (Node.Nodes (arg_execute ps `Math));
   open_node_list ps `Math)
  
let right_delim ps =
  (add_node ps (Node.Nodes (close_node_list ps `Math));
   if (UCStream.next_char ps.input_stream) = 46
   then
     (UCStream.remove ps.input_stream 1;
      add_node ps
        (Node.Nodes
           [ Node.MathChar (location ps, (Close, ((-1), (-1)), ((-1), (-1)))) ]))
   else add_node ps (Node.Nodes (arg_execute ps `Math));
   let nodes =
     List.map
       (fun x ->
          match x with
          | Node.Nodes ns -> ns
          | _ ->
              (log_warn (location ps) "syntax error in left-right statement!";
               []))
       (close_node_list ps `Math)
   in add_node ps (Node.LeftRight (location ps, nodes)))
  
let fraction ps =
  let num = arg_execute ps `Math in
  let denom = arg_execute ps `Math
  in
    add_node ps
      (Node.Fraction (location ps, num, denom,
         Node.MathChar (location ps, (Open, ((-1), (-1)), ((-1), (-1)))),
         Node.MathChar (location ps, (Close, ((-1), (-1)), ((-1), (-1)))),
         (fun _ -> num_of_int (-1))))
  
let general_fraction ps =
  let left = arg_execute ps `Math in
  let right = arg_execute ps `Math in
  let thick = arg_skip ps in
  let num = arg_execute ps `Math in
  let denom = arg_execute ps `Math
  in
    if ((List.length left) <> 1) || ((List.length right) <> 1)
    then log_warn (location ps) "invalid delimiter!"
    else
      add_node ps
        (Node.Fraction (location ps, num, denom, List.hd left, List.hd right,
           thick))
  
let vert_centre ps =
  let centre env _ (* w *) h d =
    let params =
      MathLayout.get_font_params (Environment.current_math_font_params env)
        MathLayout.Text in
    let shift =
      sub_num params.FontMetric.axis_height
        (div_num (sub_num h.d_base d.d_base) num_two)
    in (num_zero, shift) in
  let body = arg_execute ps `Math
  in add_node ps (Node.PositionBox (location ps, centre, body))
  
let set_math_style style ps =
  add_node ps (Node.MathStyle (location ps, style))
  
let index_position ps =
  let pos = Array.of_list (arg_expanded ps)
  in
    if pos = str_left
    then add_node ps (Node.IndexPosition (location ps, LeftIndex))
    else
      if pos = str_right
      then add_node ps (Node.IndexPosition (location ps, RightIndex))
      else
        if pos = str_vert
        then add_node ps (Node.IndexPosition (location ps, VertIndex))
        else
          (log_error (location ps) "unknown index position `";
           log_uc_string pos;
           log_string "'!")
  
(* counters *)
let new_counter ps =
  let super = Array.of_list (opt_expanded ps []) in
  let ctr = Array.of_list (arg_expanded ps)
  in
    if (Array.length super) = 0
    then new_counter ps ctr 0 None
    else new_counter ps ctr 0 (Some super)
  
let set_counter ps =
  let ctr = Array.of_list (arg_expanded ps) in
  let arg = arg_int ps in ParseState.set_counter ps ctr arg
  
let add_to_counter ps =
  let ctr = Array.of_list (arg_expanded ps) in
  let arg = arg_int ps
  in ParseState.set_counter ps ctr ((get_counter ps ctr) + arg)
  
let format_counter ps fmt ctr =
  (* repeat the first argument of <str> <n> times *)
  let repeat n str =
    let str_stream = UCStream.of_list str in
    let arg = Parser.read_argument str_stream in
    let rec iter i = if i <= 0 then [] else arg @ (iter (i - 1)) in iter n in
  (* select the <n>-th argument of <str> *)
  let rec select n str =
    let str_stream = UCStream.of_list str in
    let rec iter arg i =
      if i <= 0 then arg else iter (Parser.read_argument str_stream) (i - 1)
    in iter (Parser.read_argument str_stream) n
  in
    match fmt with
    | [ 105 ] -> UFormat.num_to_roman (num_of_int (get_counter ps ctr)) (* "i" *)
    | [ 73 ] -> UFormat.num_to_ROMAN (num_of_int (get_counter ps ctr)) (* "I" *)
    | [ 97 ] -> UFormat.num_to_alphabetic (num_of_int (get_counter ps ctr)) (* "a" *)
    | [ 65 ] -> UFormat.num_to_ALPHABETIC (num_of_int (get_counter ps ctr)) (* "A" *)
    | 114 :: str -> repeat (get_counter ps ctr) str (* "r{...}" *)
    | 115 :: str -> select (get_counter ps ctr) str (* "s{..}..{..}" *)
    | _ ->
        UFormat.num_to_arabic 10 (num_of_int (get_counter ps ctr))
  
(* "1" *)
let get_counter ps =
  let fmt = arg_expanded ps in
  let ctr = Array.of_list (arg_expanded ps)
  in UCStream.insert_list ps.input_stream (format_counter ps fmt ctr)
  
let expand_get_counter ps _ =
  let fmt = arg_expanded ps in
  let ctr = Array.of_list (arg_expanded ps) in
  let contents = format_counter ps fmt ctr in contents @ (Macro.expand ps)
  
(* layout *)
let shipout_pages ps =
  let number = opt_int ps 0 in
  let even = Array.of_list (arg_expanded ps) in
  let odd = Array.of_list (arg_expanded ps)
  in add_node ps (Node.ShipOut (location ps, even, odd, max 0 number))
  
let new_page_layout ps =
  let name = Array.of_list (arg_expanded ps) in
  let width = arg_skip ps in
  let height = arg_skip ps
  in add_node ps (Node.NewLayout (location ps, name, width, height))
  
let next_page_layout ps =
  let layout = arg_expanded ps
  in
    add_node ps
      (Node.CommandBox (location ps,
         `PageCmd (Box.SetNextLayout (Array.of_list layout))))
  
let new_area ps =
  let name = Array.of_list (arg_expanded ps) in
  let pos_x = arg_skip ps in
  let pos_y = arg_skip ps in
  let width = arg_skip ps in
  let height = arg_skip ps in
  let max_top = arg_skip ps in
  let max_bot = arg_skip ps in
  let area_type = Array.of_list (arg_expanded ps)
  in
    if area_type = str_galley
    then
      (let param = arg_key_val ps
       in
         add_node ps
           (Node.NewArea (location ps, name, pos_x, pos_y, width, height,
              max_top, max_bot,
              `Galley (parse_galley_area_dict (location ps) param))))
    else
      if area_type = str_float
      then
        (let param = arg_key_val ps
         in
           add_node ps
             (Node.NewArea (location ps, name, pos_x, pos_y, width, height,
                max_top, max_bot,
                `Float (parse_float_area_dict (location ps) param))))
      else
        if area_type = str_footnote
        then
          (let param = arg_key_val ps
           in
             add_node ps
               (Node.NewArea (location ps, name, pos_x, pos_y, width, height,
                  max_top, max_bot,
                  `Footnote (parse_footnote_area_dict ps (location ps) param))))
        else
          if area_type = str_direct
          then
            (let code = Parser.read_argument ps.input_stream in
             let current_ps = duplicate ps in
             let stream = UCStream.of_list code in
             let f pi _ =
               (UCStream.assign current_ps.input_stream stream;
                ParseState.set_counter current_ps str_page pi.Box.pi_page_no;
                List.iter
                  (fun (m, v) ->
                     (ALParseState.set_string_global current_ps
                        (Array.of_list
                           ((UString.of_ascii "OldMark") @ (Array.to_list m)))
                        v;
                      ALParseState.set_string_global current_ps
                        (Array.of_list
                           ((UString.of_ascii "NewMark") @ (Array.to_list m)))
                        v))
                  (List.rev pi.Box.pi_old_marks);
                List.iter
                  (fun (m, v) ->
                     ALParseState.set_string_global current_ps
                       (Array.of_list
                          ((UString.of_ascii "NewMark") @ (Array.to_list m)))
                       v)
                  (List.rev pi.Box.pi_new_marks);
                ParseState.run_parser current_ps `VBox)
             in
               add_node ps
                 (Node.NewArea (location ps, name, pos_x, pos_y, width,
                    height, max_top, max_bot, `Direct f)))
          else
            (log_warn (location ps) "unknown area type ";
             log_uc_string area_type;
             log_string "!\n")
  
let new_galley ps =
  let name = Array.of_list (arg_expanded ps) in
  let measure = arg_skip ps
  in add_node ps (Node.NewGalley (location ps, name, measure))
  
let begin_galley ps =
  let name = Array.of_list (arg_expanded ps)
  in
    (open_node_list ps `Galley;
     add_node ps (Node.AddToGalley (location ps, name, [])))
  
let end_galley ps =
  (Mode.leave_par_mode ps;
   match close_node_list ps `Galley with
   | [] -> log_warn (location ps) "\\endgalley without \\begingalley!"
   | n :: ns ->
       (match n with
        | Node.AddToGalley (loc, name, []) ->
            add_node ps (Node.AddToGalley (loc, name, ns))
        | _ -> assert false))
  
let float_box ps =
  let name = Array.of_list (arg_expanded ps) in
  let body = arg_execute ps `VBox
  in add_node ps (Node.Float (location ps, name, body))
  
let float_par ps =
  let name = Array.of_list (arg_expanded ps) in
  let body = arg_execute ps `HBox
  in add_node ps (Node.Float (location ps, name, body))
  
let float_galley ps =
  let name = Array.of_list (arg_expanded ps) in
  let body = arg_execute ps `Galley
  in add_node ps (Node.Float (location ps, name, body))
  
(*
value annotate_paragraph ps = do
{
  (* <left> and <right> should evaluate to a box of width zero. *)

  let left  = arg_execute ps `HBox;
  let right = arg_execute ps `HBox;

  let annotate env boxes = do
  {
    let (b, get) = Builder.simple_builder ();
    let _        = Evaluate.eval_node_list env b left;

    Builder.add_box_list b boxes;

    let _ = Evaluate.eval_node_list env b right;

    get ()
  };

  add_node ps (Node.Command (location ps)
    (Environment.set_par_params
      (None, None, None, None, None, None, None, Some annotate)))
};

value set_par_shape ps = do
{
  let read_next stream = do
  {
    let (a,b) = Parser.read_range stream;

    if UCStream.next_char stream = 58 then do
    {
      UCStream.remove stream 1;
      Parser.skip_spaces stream;

      (int_of_num (ceiling_num a), (int_of_num (floor_num b)), Parser.read_skip stream)
    }
    else do
    {
      log_warn (location ps) "Invalid par shape!";
      log_int (UCStream.next_char stream);
      (-1, -1, (fun _ -> num_zero))
    }
  };
  let rec parse_shape stream = do
  {
    Parser.skip_spaces stream;

    if UCStream.next_char stream >= 0 then do
    {
      let (a,b,s) = read_next stream;

      if a < 0 then
        []
      else do
      {
        Parser.skip_spaces stream;

        match UCStream.next_char stream with
        [ 44 | 59 -> do
          {
            UCStream.remove stream 1;
            [(a,b,s) :: parse_shape stream]
          }
        | (-1) -> [(a,b,s)]
        | _    -> do
          {
            log_warn (location ps) "Invalid par shape!";
            [(a,b,s)]
          }
        ]
      }
    }
    else
      []
  };

  let left_shape  = arg_expanded ps;
  let right_shape = arg_expanded ps;

  let left_indent  = parse_shape (UCStream.of_list left_shape);
  let right_indent = parse_shape (UCStream.of_list right_shape);

  let rec lookup n list = match list with
  [ []              -> fun _ -> num_zero
  | [(a,b,s) :: ls] -> if a <= n && n <= b then
                         s
                       else
                         lookup n ls
  ];

  let par_shape env n = (lookup n left_indent env, lookup n right_indent env);

  add_node ps (Node.Command (location ps)
    (Environment.set_par_params
      (None, None, None, None, None, Some par_shape, None, None)))
};
*)
let no_indent ps =
  (Mode.ensure_par_mode ps;
   add_node ps
     (Node.Command (location ps,
        Environment.set_current_par_params
          (None, (Some (fun _ -> dim_zero)), None, None, None, None, None,
           None, None, None))))
  
let indent ps =
  Mode.ensure_par_mode ps;
  add_node ps
    (Node.Command
       (location ps,
        (fun loc env ->
           let p = Galley.par_params (Environment.current_galley env) in
             Environment.set_current_par_params
               (None, (Some (fun _ -> p.ParLayout.par_indent)), None, None,
                None, None, None, None, None, None)
               loc env)))
  
let ensure_vskip ps =
  let skip = arg_skip ps in
  let f env glue =
    let min_skip = skip env in
    let current_skip =
      List.fold_left
        (fun x b -> xdim_add_dim (xdim_add_dim x b.b_height) b.b_depth)
        xdim_zero glue
    in
      if lt_num current_skip.xd_base min_skip
      then
        (new_glue_box dim_zero
           (fixed_dim (sub_num min_skip current_skip.xd_base)) false true) ::
          glue
      else glue
  in add_node ps (Node.ModifyGalleyGlue (location ps, f))

(*-----------------------------------------------------------------------*)  
(* misc *)

let relax _ = ()
  
let expand_relax ps _ = Macro.expand ps
  
(* The default command. Note that it has to remove itself from the input stream. *)
let cc_put_char ps =
  Mode.ensure_par_mode ps;
  let c = UCStream.pop ps.input_stream in
    if current_mode ps = `Math then
      add_math_char ps (get_math_code ps c)
    else
      add_node ps (Node.Letter (location ps, c))
  
let put_char ps =
  Mode.ensure_par_mode ps;
  let c = arg_int ps in
    if current_mode ps = `Math then
      add_math_char ps (get_math_code ps c)
    else
      add_node ps (Node.Letter (location ps, c))
  
let expand_put_char ps _ =
  let char = arg_expanded ps in
    (Parser.str_to_int char) :: (Macro.expand ps)
  
let put_glyph ps =
  Mode.ensure_par_mode ps;

  (* This should be a glyph name. *)
  let s = Array.of_list (arg_expanded ps) in

  (* Interpretation as a glyph index number, mainly for compatibility with ANT. *)
  let c = int_of_num (integer_num (Parser.str_expr_to_num (Array.to_list s))) in

    add_node ps (Node.Glyph (location ps, c, s))
  
let put_math_char ps =
  (Mode.ensure_par_mode ps;
   let mcode =
     match UString.to_string (arg_expanded ps) with
     | "letter" -> NoMath
     | "ordinary" -> Ordinary
     | "binop" -> BinOp
     | "relation" -> Relation
     | "operator" -> Operator
     | "punct" -> Punct
     | "open" -> Open
     | "close" -> Close
     | "inner" -> Inner
     | "subscript" -> SubScript
     | "superscript" -> SuperScript
     | x ->
         (log_warn (location ps) ("unknown math code `" ^ (x ^ "'!"));
          NoMath) in
   let f1 = arg_int ps in
   let c1 = arg_int ps in
   let f2 = arg_int ps in
   let c2 = arg_int ps in add_math_char ps (mcode, (f1, f2), (c1, c2)))
  
let nobreak_space ps =
  match current_mode ps with
    | `Paragraph | `HBox | `LRBox | `RLBox ->
        add_node ps (Node.WordEnd (location ps, true));
        add_node ps (Node.Space (location ps))
    | _ -> ()
  
let break_space ps =
  begin
    match current_mode ps with
      | `Paragraph | `HBox | `LRBox | `RLBox ->
          add_node ps (Node.WordEnd (location ps, false));
          add_node ps (Node.Break (location ps, None, false, [], [], []));
          add_node ps (Node.Space (location ps))
      | _ -> ()
  end;
  Parser.skip_blanks ps.input_stream
  
let expand_break_space ps _ = 32 :: (Macro.expand ps)
  
let newline ps =
  begin
    match current_mode ps with
      | `Paragraph | `HBox | `LRBox | `RLBox ->
          add_node ps (Node.WordEnd (location ps, false));
          add_node ps (Node.Break (location ps, None, false, [], [], []));
          add_node ps (Node.Space (location ps))
      | _ -> ()
  end;
  Parser.skip_spaces ps.input_stream;
  Parser.newline_to_par ps.input_stream
  
let expand_newline ps _ = 10 :: (Macro.expand ps)
  
let literal ps =
  let rec read_argument i =
    if (UCStream.get_char ps.input_stream i) = (-1) then
      []
    else if Parser.match_substring ps.input_stream (UString.of_ascii "\\endliteral") i then
      begin
        let arg = UCStream.take ps.input_stream i
        in UCStream.remove ps.input_stream (i + 11);
          arg
      end
    else
      read_argument (i + 1)
  in
  let arg = read_argument 0 in
    Mode.ensure_par_mode ps;
    List.iter (fun c -> add_node ps (Node.Letter (location ps, c))) arg
  
let expand_literal ps _ =
  let rec read_argument i =
    if (UCStream.get_char ps.input_stream i) = (-1)
    then []
    else
      if
        Parser.match_substring ps.input_stream
          (UString.of_ascii "\\endliteral") i
      then
        (let arg = UCStream.take ps.input_stream i
         in (UCStream.remove ps.input_stream (i + 11); arg))
      else read_argument (i + 1) in
  let arg = read_argument 0 in arg @ (Macro.expand ps)
  
let comment ps = Parser.skip_comment ps.input_stream
  
let escape ps =
  Macro.execute_command ps (Parser.read_token_tail ps.input_stream)
  
(* An apostrophe is handled specially in math-mode. *)
let apostrophe ps =
  if current_mode ps = `Math then
    prime ps
  else
    add_node ps (Node.Letter (location ps, 39))
  
let include_file ?(kpathsea = true) ps =
  let name = arg_expanded ps in
  let name_string = UniText.of_int_list name in
    if not kpathsea then
      UCStream.include_file ps.input_stream name_string
    else
      let resource =
        try
          Some (KPathSea.find_resource (UniText.to_string name_string))
        with
          | Not_found ->
              log_error (location ps) ("Resource not found: '");
              log_uc_list name;
              log_string "'!";
              None
      in
        match resource with
          | None -> ()
          | Some r -> UCStream.include_file ps.input_stream (UniText.of_string r)
  
let end_input ps = UCStream.clear ps.input_stream
  
(*-----------------------------------------------------------------------*)  
(* colours *)

let set_grey_colour ps =
  let bound x =
    if lt_num x num_zero
    then (log_warn (location ps) "negative colour value set to 0!"; num_zero)
    else
      if gt_num x num_one
      then (log_warn (location ps) "colour value set to 1!"; num_one)
      else x in
  let x = bound (arg_num ps)
  in
    add_node ps
      (Node.Command (location ps, Environment.set_colour (Graphic.Grey x)))
  
let set_rgb_colour ps =
  let bound x =
    if lt_num x num_zero
    then (log_warn (location ps) "negative colour value set to 0!"; num_zero)
    else
      if gt_num x num_one
      then (log_warn (location ps) "colour value set to 1!"; num_one)
      else x in
  let r = bound (arg_num ps) in
  let g = bound (arg_num ps) in
  let b = bound (arg_num ps)
  in
    add_node ps
      (Node.Command (location ps,
         Environment.set_colour (Graphic.RGB (r, g, b))))
  
let set_cmyk_colour ps =
  let bound x =
    if lt_num x num_zero
    then (log_warn (location ps) "negative colour value set to 0!"; num_zero)
    else
      if gt_num x num_one
      then (log_warn (location ps) "colour value set to 1!"; num_one)
      else x in
  let c = bound (arg_num ps) in
  let m = bound (arg_num ps) in
  let y = bound (arg_num ps) in
  let k = bound (arg_num ps)
  in
    add_node ps
      (Node.Command (location ps,
         Environment.set_colour (Graphic.CMYK (c, m, y, k))))
  
(*-----------------------------------------------------------------------*)  
(* al *)

let al_declarations ps =
  let rec read_argument i =
    if UCStream.get_char ps.input_stream i < 0 then
      []
    else if Parser.match_substring ps.input_stream (UString.of_ascii "\\endALdeclarations") i then
      let arg = UCStream.take ps.input_stream i in
        begin
          UCStream.remove ps.input_stream (i + 18);
          arg
        end
    else
      read_argument (i + 1)
  in
    (* After reading the |\beginALdeclarations| command the parser
       might have inserted a |\par|. We need to remove it. *)
    Parser.skip_spaces ps.input_stream;
    if Parser.match_substring ps.input_stream (UString.of_ascii "\\par") 0 then
      UCStream.remove ps.input_stream 4;
    let loc = location ps in
    let arg = read_argument 0 in
    let str = UCStream.of_list arg in
      UCStream.set_location str loc false;
      try
        Machine.execute_declarations ps.al_scope str
      with
        | VMTypes.Syntax_error (loc, msg) ->
            log_warn loc (UString.to_string (Array.to_list msg))
        
let al_macro ps =
  let loc = location ps in
  let expr = arg_expanded ps in
  let str = UCStream.of_list expr in
    UCStream.set_location str loc true;
    try
      let result =
        Machine.evaluate_string_expr "\\ALmacro" ps.al_scope str
      in
        UCStream.insert_list ps.input_stream result
    with
      | VMTypes.Syntax_error (loc, msg) ->
          log_warn loc (UString.to_string (Array.to_list msg))
      | VMTypes.Runtime_error msg ->
          log_warn (location ps) (UString.to_string (Array.to_list msg))
      
let expand_al_macro ps _ =
  let loc = location ps in
  let expr = arg_expanded ps in
  let str = UCStream.of_list expr in
    UCStream.set_location str loc true;
    let result =
      try Machine.evaluate_string_expr "\\ALmacro" ps.al_scope str
      with
        | VMTypes.Syntax_error (loc, msg) ->
            log_warn loc (UString.to_string (Array.to_list msg));
            []
        | VMTypes.Runtime_error msg ->
            log_warn (location ps) (UString.to_string (Array.to_list msg));
            []
    in
      result @ (Macro.expand ps)
      
let al_command ps =
  let loc = location ps in
  let expr = Parser.read_argument ps.input_stream in
  let str = UCStream.of_list expr in
    UCStream.set_location str loc false;
    ALParseState.execute_ps_command "\\ALcommand" str ps
      
(* math code table *)
(* all undefined characters are ordinary symbols of family 1 *)
let default_math_codes_xx =
  [| (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
     (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0))
  |]
  
let default_math_codes_00 =
  [| (* space *) (* exclamation mark *) (* quotation mark *)
    (* number sign *) (* dollar sign *) (* percent sign *) (* ampersand *)
    (* apostrophe *) (* left parenthesis *) (* right parenthesis *)
    (* asterisk *) (* plus sign *) (* comma *) (* hyphen-minus *)
    (* period *) (* slash *) (* digit zero *) (* digit one *) (* digit two *)
    (* digit three *) (* digit four *) (* digit five *) (* digit six *)
    (* digit seven *) (* digit eight *) (* digit nine *) (* colon *)
    (* semicolon *) (* less-than sign *) (* equals sign *)
    (* greater-than sign *) (* question mark *) (* commercial at *)
    (* latin capital letter a *) (* latin capital letter b *)
    (* latin capital letter c *) (* latin capital letter d *)
    (* latin capital letter e *) (* latin capital letter f *)
    (* latin capital letter g *) (* latin capital letter h *)
    (* latin capital letter i *) (* latin capital letter j *)
    (* latin capital letter k *) (* latin capital letter l *)
    (* latin capital letter m *) (* latin capital letter n *)
    (* latin capital letter o *) (* latin capital letter p *)
    (* latin capital letter q *) (* latin capital letter r *)
    (* latin capital letter s *) (* latin capital letter t *)
    (* latin capital letter u *) (* latin capital letter v *)
    (* latin capital letter w *) (* latin capital letter x *)
    (* latin capital letter y *) (* latin capital letter z *)
    (* left square bracket *) (* backslash *) (* right square bracket *)
    (* circumflex accent *) (* underline *) (* grave accent *)
    (* latin small letter a *) (* latin small letter b *)
    (* latin small letter c *) (* latin small letter d *)
    (* latin small letter e *) (* latin small letter f *)
    (* latin small letter g *) (* latin small letter h *)
    (* latin small letter i *) (* latin small letter j *)
    (* latin small letter k *) (* latin small letter l *)
    (* latin small letter m *) (* latin small letter n *)
    (* latin small letter o *) (* latin small letter p *)
    (* latin small letter q *) (* latin small letter r *)
    (* latin small letter s *) (* latin small letter t *)
    (* latin small letter u *) (* latin small letter v *)
    (* latin small letter w *) (* latin small letter x *)
    (* latin small letter y *) (* latin small letter z *)
    (* left curly bracket *) (* vertical line *) (* right curly bracket *)
    (* tilde *) (* no-break space *) (* inverted exclamation mark *)
    (* cent sign *) (* pound sign *) (* currency sign *) (* yen sign *)
    (* broken bar *) (* section sign *) (* diaeresis *) (* copyright sign *)
    (* feminine ordinal indicator *) (* left guillemet *) (* not sign *)
    (* soft hyphen *) (* registered trade mark sign *) (* macron, overline *)
    (* degree sign *) (* plus-minus sign *) (* superscript two *)
    (* superscript three *) (* acute accent *) (* micro sign *)
    (* paragraph sign *) (* middle dot, kana conjoctive *) (* cedilla *)
    (* superscript one *) (* masculine ordinal indicator *)
    (* right guillemet *) (* vulgar fraction one quarter *)
    (* vulgar fraction one half *) (* vulgar fraction three quarters *)
    (* inverted question mark *)
    (* latin capital letter a with grave accent *)
    (* latin capital letter a with acute accent *)
    (* latin capital letter a with circumflex accent *)
    (* latin capital letter a with tilde *)
    (* latin capital letter a with diaeresis *)
    (* latin capital letter a with ring above *)
    (* latin capital letter a with e *)
    (* latin capital letter c with cedilla *)
    (* latin capital letter e with grave accent *)
    (* latin capital letter e with acute accent *)
    (* latin capital letter e with circumflex accent *)
    (* latin capital letter e with diaeresis *)
    (* latin capital letter i with grave accent *)
    (* latin capital letter i with acute accent *)
    (* latin capital letter i with circumflex accent *)
    (* latin capital letter i with diaeresis *)
    (* latin capital letter eth *) (* latin capital letter n with tilde *)
    (* latin capital letter o with grave accent *)
    (* latin capital letter o with acute accent *)
    (* latin capital letter o with circumflex accent *)
    (* latin capital letter o with tilde *)
    (* latin capital letter o with diaeresis *) (* multiplication sign *)
    (* latin capital letter o with oblique stroke *)
    (* latin capital letter u with grave accent *)
    (* latin capital letter u with acute accent *)
    (* latin capital letter u with circumflex accent *)
    (* latin capital letter u with diaeresis *)
    (* latin capital letter y with acute accent *)
    (* latin capital letter thorn *) (* latin small letter sharp s *)
    (* latin small letter a with grave accent *)
    (* latin small letter a with acute accent *)
    (* latin small letter a with circumflex accent *)
    (* latin small letter a with tilde *)
    (* latin small letter a with diaeresis *)
    (* latin small letter a with ring above *)
    (* latin small letter a with e *) (* latin small letter c with cedilla *)
    (* latin small letter e with grave accent *)
    (* latin small letter e with acute accent *)
    (* latin small letter e with circumflex accent *)
    (* latin small letter e with diaeresis *)
    (* latin small letter i with grave accent *)
    (* latin small letter i with acute accent *)
    (* latin small letter i with circumflex accent *)
    (* latin small letter i with diaeresis *) (* latin small letter eth *)
    (* latin small letter n with tilde *)
    (* latin small letter o with grave accent *)
    (* latin small letter o with acute accent *)
    (* latin small letter o with circumflex accent *)
    (* latin small letter o with tilde *)
    (* latin small letter o with diaeresis *) (* division sign *)
    (* latin small letter o with oblique stroke *)
    (* latin small letter u with grave accent *)
    (* latin small letter u with acute accent *)
    (* latin small letter u with circumflex accent *)
    (* latin small letter u with diaeresis *)
    (* latin small letter y with acute accent *) (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (Ordinary, (0, 0), (33, 33)); (Ordinary, (0, 0), (34, 34));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (Open, (0, 3), (40, 0));
    (Close, (0, 3), (41, 1)); (BinOp, (2, 2), (3, 3));
    (BinOp, (0, 0), (43, 43)); (Punct, (1, 1), (59, 59));
    (BinOp, (2, 2), (0, 0)); (Ordinary, (1, 1), (58, 58));
    (Ordinary, (1, 1), (61, 61)); (Ordinary, (0, 0), (48, 48));
    (Ordinary, (0, 0), (49, 49)); (Ordinary, (0, 0), (50, 50));
    (Ordinary, (0, 0), (51, 51)); (Ordinary, (0, 0), (52, 52));
    (Ordinary, (0, 0), (53, 53)); (Ordinary, (0, 0), (54, 54));
    (Ordinary, (0, 0), (55, 55)); (Ordinary, (0, 0), (56, 56));
    (Ordinary, (0, 0), (57, 57)); (Relation, (0, 0), (58, 58));
    (Punct, (0, 0), (59, 59)); (Relation, (1, 1), (60, 60));
    (Relation, (0, 0), (61, 61)); (Relation, (1, 1), (62, 62));
    (Ordinary, (0, 0), (63, 63)); (Ordinary, (0, 0), (64, 64));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (Open, (0, 3), (91, 2)); (NoMath, (0, 0), (0, 0));
    (Close, (0, 3), (93, 3)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (Ordinary, (0, 0), (96, 96));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (Ordinary, (2, 2), (106, 106));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (NoMath, (0, 0), (0, 0)); (NoMath, (0, 0), (0, 0));
    (* latin small letter thorn *) (NoMath, (0, 0), (0, 0))
  |]
  
(* latin small letter y with diaeresis *)
let default_math_code_table =
  CharMap.build
    [| default_math_codes_00; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx; default_math_codes_xx; default_math_codes_xx;
      default_math_codes_xx
    |]
  
(*-----------------------------------------------------------------------*)  
(* python *)

let python_section ps =
  let rec read_argument i =
    if UCStream.get_char ps.input_stream i < 0 then
      []
    else if Parser.match_substring ps.input_stream (UString.of_ascii "\\endpython") i then
      let arg = UCStream.take ps.input_stream i in
        begin
          UCStream.remove ps.input_stream (i + 10);
          arg
        end
    else
      read_argument (i + 1)
  in
    (* After reading the |\beginpython| command the parser
       might have inserted a |\par|. We need to remove it. *)
    Parser.skip_spaces ps.input_stream;
    if Parser.match_substring ps.input_stream (UString.of_ascii "\\par") 0 then
      UCStream.remove ps.input_stream 4;
    let loc = location ps in
    let arg = read_argument 0 in
    let str = UCStream.of_list arg in
      UCStream.set_location str loc false;

      (* Call the wrapper function kt.kt_exec(). *)
      let kt = py_module "kt" in
      let s = UString.uc_string_to_utf8 (Array.of_list arg) in
      let py_ps = pythonize_parse_state ps in
      let py_code = pythonize_string s in
      let exit_code = py_module_call kt "kt_exec" (pytuple2 (py_ps, py_code)) in
        if pyint_asint exit_code <> 0 then
          failwith "Failing due to a Python error."

(*-----------------------------------------------------------------------*)  
(* binding of primitive commands *****************************************************************)

let initialise ps =
  let def_expandable_cmd name cmd exp =
    ParseState.define_command ps (UString.of_ascii name)
      { ParseState.execute = cmd; ParseState.expand = exp; } in
  let def_unexpandable_cmd name cmd =
    def_expandable_cmd name cmd Macro.noexpand in
  let def_expandable_pat name cmd exp =
    ParseState.define_pattern ps (UString.of_ascii name)
      { ParseState.execute = cmd; ParseState.expand = exp; } in
  let def_unexpandable_pat name cmd =
    def_expandable_pat name cmd Macro.noexpand in
  let def_macro name body =
    def_expandable_cmd name (Macro.execute_macro [] (UString.of_ascii body))
      (Macro.expand_macro [] (UString.of_ascii body)) in
  let def_macro_arg name body =
    let t = Macro.parse_arg_template Location.empty_location [ 109 ]
    in
      (* m *)
      def_expandable_cmd name (Macro.execute_macro t (UString.of_ascii body))
        (Macro.expand_macro t (UString.of_ascii body)) in
  let def_math_char name code family char =
    def_unexpandable_cmd name
      (fun ps -> add_math_char ps (code, (family, family), (char, char))) in
  let def_math_delim name code small_family small_char large_family
      large_char =
    def_unexpandable_cmd name
      (fun ps ->
         add_math_char ps
           (code, (small_family, large_family), (small_char, large_char))) in
  let def_math_acc name family char =
    def_unexpandable_cmd name (fun ps -> math_accent ps family char)
  in
    ParseState.set_default_char_cmd ps
      {
        ParseState.execute = cc_put_char;
        ParseState.expand = Macro.noexpand;
      };
    ALBindings.add_primitives ps.ParseState.job ps.ParseState.al_scope;

    (* commands *)
    def_unexpandable_cmd "\\begingroup" Group.begin_group;
    def_unexpandable_cmd "\\endgroup" Group.end_group;
    def_unexpandable_pat "{" Group.begin_group;
    def_unexpandable_pat "}" Group.end_group;
    def_unexpandable_cmd "\\setparameter" set_param;
    def_unexpandable_cmd "\\setmathfont" set_math_font;
    def_unexpandable_cmd "\\setmark" set_mark;
    def_unexpandable_cmd "\\endgraf" Mode.leave_par_mode;
    def_unexpandable_cmd "\\par" Mode.leave_par_mode;
    def_unexpandable_cmd "\\shipoutpages" shipout_pages;
    def_unexpandable_cmd "\\newpagelayout" new_page_layout;
    def_unexpandable_cmd "\\nextpagelayout" next_page_layout;
    def_unexpandable_cmd "\\newpagearea" new_area;
    def_unexpandable_cmd "\\newgalley" new_galley;
    def_unexpandable_cmd "\\begingalley" begin_galley;
    def_unexpandable_cmd "\\endgalley" end_galley;
    def_unexpandable_cmd "\\floatbox" float_box;
    def_unexpandable_cmd "\\floatpar" float_par;
    def_unexpandable_cmd "\\floatgalley" float_galley;
    def_unexpandable_cmd "\\noindent" no_indent;
    def_unexpandable_cmd "\\indent" indent;
    def_unexpandable_cmd "\\ensurevskip" ensure_vskip;

    (* control *)
    def_expandable_cmd "\\relax" relax expand_relax;
    def_unexpandable_cmd "\\definecommand" define_command;
    def_unexpandable_cmd "\\definepattern" define_pattern;
    def_unexpandable_cmd "\\defineenvironment" define_environment;
    def_unexpandable_cmd "\\savecommand" save_command;
    def_unexpandable_cmd "\\restorecommand" restore_command;
    def_unexpandable_cmd "\\savepattern" save_pattern;
    def_unexpandable_cmd "\\restorepattern" restore_pattern;
    def_unexpandable_cmd "\\begin" begin_environment;
    def_unexpandable_cmd "\\end" end_environment;
    def_unexpandable_cmd "\\include" include_file;
    def_unexpandable_cmd "\\endinput" end_input;
    def_macro "\\jobname" ps.job.Job.jobname;
    def_unexpandable_cmd "\\beginALdeclarations" al_declarations;
    def_unexpandable_cmd "\\beginpython" python_section;
    def_expandable_cmd "\\ALmacro" al_macro expand_al_macro;
    def_unexpandable_cmd "\\ALcommand" al_command;

    (* characters *)
    def_expandable_cmd "\\char" put_char expand_put_char;
    def_unexpandable_cmd "\\glyph" put_glyph;
    def_unexpandable_cmd "\\mathchar" put_math_char;
    def_expandable_pat "\000" relax expand_relax;
    def_expandable_pat "\001" break_space expand_break_space;
    def_expandable_pat "\002" break_space expand_break_space;
    def_expandable_pat "\003" break_space expand_break_space;
    def_expandable_pat "\004" break_space expand_break_space;
    def_expandable_pat "\005" break_space expand_break_space;
    def_expandable_pat "\006" break_space expand_break_space;
    def_expandable_pat "\007" break_space expand_break_space;
    def_expandable_pat "\008" break_space expand_break_space;
    def_expandable_pat "\009" break_space expand_break_space;
    def_expandable_pat "\010" newline expand_newline;
    def_expandable_pat "\011" break_space expand_break_space;
    def_expandable_pat "\012" break_space expand_break_space;
    def_expandable_pat "\013" break_space expand_break_space;
    def_expandable_pat "\014" break_space expand_break_space;
    def_expandable_pat "\015" break_space expand_break_space;
    def_expandable_pat "\016" break_space expand_break_space;
    def_expandable_pat "\017" break_space expand_break_space;
    def_expandable_pat "\018" break_space expand_break_space;
    def_expandable_pat "\019" break_space expand_break_space;
    def_expandable_pat "\020" break_space expand_break_space;
    def_expandable_pat "\021" break_space expand_break_space;
    def_expandable_pat "\022" break_space expand_break_space;
    def_expandable_pat "\023" break_space expand_break_space;
    def_expandable_pat "\024" break_space expand_break_space;
    def_expandable_pat "\025" break_space expand_break_space;
    def_expandable_pat "\026" break_space expand_break_space;
    def_expandable_pat "\027" break_space expand_break_space;
    def_expandable_pat "\028" break_space expand_break_space;
    def_expandable_pat "\029" break_space expand_break_space;
    def_expandable_pat "\030" break_space expand_break_space;
    def_expandable_pat "\031" break_space expand_break_space;
    def_expandable_pat "\032" break_space expand_break_space;
    def_unexpandable_pat "%" comment;
    def_unexpandable_pat "\\" escape;
    def_unexpandable_pat "'" apostrophe;
    def_unexpandable_pat "~" nobreak_space;
    def_expandable_cmd "\\beginliteral" literal expand_literal;

    (* counters *)
    def_unexpandable_cmd "\\newcounter" new_counter;
    def_unexpandable_cmd "\\setcounter" set_counter;
    def_unexpandable_cmd "\\addtocounter" add_to_counter;
    def_expandable_cmd "\\getcounter" get_counter expand_get_counter;
    ParseState.new_counter ps (UString.uc_string_of_ascii "year")
      (ps.job.Job.time.Unix.tm_year + 1900) None;
    ParseState.new_counter ps (UString.uc_string_of_ascii "month")
      (ps.job.Job.time.Unix.tm_mon + 1) None;
    ParseState.new_counter ps (UString.uc_string_of_ascii "day")
      ps.job.Job.time.Unix.tm_mday None;
    ParseState.new_counter ps (UString.uc_string_of_ascii "day-of-week")
      ps.job.Job.time.Unix.tm_wday None;

    (* boxes *)
    def_unexpandable_cmd "\\hskip" hskip;
    def_unexpandable_cmd "\\vskip" vskip;
    def_unexpandable_cmd "\\kern" kern;
    def_unexpandable_cmd "\\hbox" (hbox `Default);
    def_unexpandable_cmd "\\lrbox" (hbox `LR);
    def_unexpandable_cmd "\\rlbox" (hbox `RL);
    def_unexpandable_cmd "\\vbox" vbox;
    def_unexpandable_cmd "\\phantom" phantom;
    def_unexpandable_cmd "\\hphantom" hphantom;
    def_unexpandable_cmd "\\vphantom" vphantom;
    def_unexpandable_cmd "\\hleaders" hleaders;
    def_unexpandable_cmd "\\vadjust" vinsert;
    def_unexpandable_cmd "\\rule" rule;
    def_unexpandable_cmd "\\image" image;
    def_unexpandable_cmd "\\parbox" parbox;
    def_unexpandable_cmd "\\accent" accent;
    def_unexpandable_cmd "\\penalty" penalty;
    def_unexpandable_cmd "\\discretionary" discretionary;

    (* tables *)
    def_unexpandable_cmd "\\newtableentry" new_table_entry;
    def_unexpandable_cmd "\\newtablerow" new_table_row;
    def_unexpandable_cmd "\\begintable" begin_table;
    def_unexpandable_cmd "\\endtable" end_table;

    (* fonts *)
    def_macro "\\FontFamilyRoman" "Computer Modern Roman";
    def_macro "\\FontFamilySans" "Computer Modern Sans Serif";
    def_macro "\\FontFamilyTypewriter" "Computer Modern Typewriter";
    def_macro "\\FontFamilyMath" "Computer Modern Math Italic";
    def_macro "\\FontFamilySymbols" "Computer Modern Math Symbols";
    def_macro "\\FontFamilyExtensions" "Computer Modern Math Extensions";
    def_macro "\\FontSeriesMedium" "medium";
    def_macro "\\FontSeriesBold" "bold extended";
    def_macro "\\FontShapeUpright" "normal";
    def_macro "\\FontShapeItalic" "italic";
    def_macro "\\FontShapeSlanted" "slanted";
    def_macro "\\FontShapeSmallCaps" "small caps";
    def_macro "\\FontSizeTiny" "5";
    def_macro "\\FontSizeScript" "7";
    def_macro "\\FontSizeFootnote" "8";
    def_macro "\\FontSizeSmall" "9";
    def_macro "\\FontSizeNormal" "10";
    def_macro "\\FontSizeLargeI" "12";
    def_macro "\\FontSizeLargeII" "14.4";
    def_macro "\\FontSizeLargeIII" "17.28";
    def_macro "\\FontSizeHugeI" "20.74";
    def_macro "\\FontSizeHugeII" "24.88";
    def_macro "\\rmfamily"
      "\\setparameter{font}{ family = \\FontFamilyRoman }";
    def_macro "\\sffamily"
      "\\setparameter{font}{ family = \\FontFamilySans }";
    def_macro "\\ttfamily"
      "\\setparameter{font}{ family = \\FontFamilyTypewriter }";
    def_macro "\\mdseries"
      "\\setparameter{font}{ series = \\FontSeriesMedium }";
    def_macro "\\bfseries"
      "\\setparameter{font}{ series = \\FontSeriesBold }";
    def_macro "\\upshape"
      "\\setparameter{font}{ shape = \\FontShapeUpright }";
    def_macro "\\itshape"
      "\\setparameter{font}{ shape = \\FontShapeItalic }";
    def_macro "\\slshape"
      "\\setparameter{font}{ shape = \\FontShapeSlanted }";
    def_macro "\\scshape"
      "\\setparameter{font}{ shape = \\FontShapeSmallCaps }";
    def_macro "\\tiny" "\\setparameter{font}{ size = \\FontSizeTiny }";
    def_macro "\\scriptsize"
      "\\setparameter{font}{ size = \\FontSizeScript }";
    def_macro "\\footnotesize"
      "\\setparameter{font}{ size = \\FontSizeFootnote }";
    def_macro "\\small" "\\setparameter{font}{ size = \\FontSizeSmall }";
    def_macro "\\normalsize"
      "\\setparameter{font}{ size = \\FontSizeNormal }";
    def_macro "\\large" "\\setparameter{font}{ size = \\FontSizeLargeI }";
    def_macro "\\Large" "\\setparameter{font}{ size = \\FontSizeLargeII }";
    def_macro "\\LARGE" "\\setparameter{font}{ size = \\FontSizeLargeIII }";
    def_macro "\\huge" "\\setparameter{font}{ size = \\FontSizeHugeI }";
    def_macro "\\Huge" "\\setparameter{font}{ size = \\FontSizeHugeII }";
    def_macro "\\normalfont"
      "\\setparameter{font}{family = \\FontFamilyRoman; series = \\FontSeriesMedium; shape = \\FontShapeUpright}";

    (* colours *)
    def_unexpandable_cmd "\\setgreycolour" set_grey_colour;
    def_unexpandable_cmd "\\setrgbcolour" set_rgb_colour;
    def_unexpandable_cmd "\\setcmykcolour" set_cmyk_colour;

    (* accents *)
    def_macro_arg "\\`" "\\accent{18}{#1}";
    def_macro_arg "\\'" "\\accent{19}{#1}";
    def_macro_arg "\\v" "\\accent{20}{#1}";
    def_macro_arg "\\u" "\\accent{21}{#1}";
    def_macro_arg "\\=" "\\accent{22}{#1}";
    def_macro_arg "\\^" "\\accent{94}{#1}";
    def_macro_arg "\\." "\\accent{95}{#1}";
    def_macro_arg "\\H" "\\accent{125}{#1}";
    def_macro_arg "\\~" "\\accent{126}{#1}";
    def_macro_arg "\\\"" "\\accent{127}{#1}";

    (* math *)
    def_unexpandable_pat "$" toggle_math;
    def_unexpandable_pat "_" sub_script;
    def_unexpandable_pat "^" super_script;
    def_unexpandable_cmd "\\frac" fraction;
    def_unexpandable_cmd "\\genfrac" general_fraction;
    def_unexpandable_cmd "\\overline" overline;
    def_unexpandable_cmd "\\underline" underline;
    def_unexpandable_cmd "\\left" left_delim;
    def_unexpandable_cmd "\\middle" mid_delim;
    def_unexpandable_cmd "\\right" right_delim;
    def_unexpandable_cmd "\\vcentre" vert_centre;
    def_unexpandable_cmd "\\indexposition" index_position;
    def_macro "\\limits" "\\indexposition{vert}";
    def_macro "\\nolimits" "\\indexposition{right}";
    def_unexpandable_cmd "\\displaystyle"
      (set_math_style MathLayout.Display);
    def_unexpandable_cmd "\\textstyle" (set_math_style MathLayout.Text);
    def_unexpandable_cmd "\\scriptstyle" (set_math_style MathLayout.Script);
    def_unexpandable_cmd "\\scriptscriptstyle"
      (set_math_style MathLayout.Script2);
    def_unexpandable_cmd "\\beginmath" Mode.begin_math;
    def_unexpandable_cmd "\\endmath" Mode.end_math;
    def_unexpandable_cmd "\\begintext" begin_text;
    def_unexpandable_cmd "\\endtext" end_text;
    def_unexpandable_cmd "\\mathord"
      (fun ps -> set_math_code ps Box.Ordinary);
    def_unexpandable_cmd "\\mathop"
      (fun ps -> set_math_code ps Box.Operator);
    def_unexpandable_cmd "\\mathbin" (fun ps -> set_math_code ps Box.BinOp);
    def_unexpandable_cmd "\\mathrel"
      (fun ps -> set_math_code ps Box.Relation);
    def_unexpandable_cmd "\\mathopen" (fun ps -> set_math_code ps Box.Open);
    def_unexpandable_cmd "\\mathclose"
      (fun ps -> set_math_code ps Box.Close);
    def_unexpandable_cmd "\\mathpunct"
      (fun ps -> set_math_code ps Box.Punct);
    def_unexpandable_cmd "\\mathinner"
      (fun ps -> set_math_code ps Box.Inner);

    (* math symbols *)
    def_unexpandable_cmd "\\sqrt" (fun ps -> put_root ps 2 112 3 112);
    def_math_char "\\alpha" Box.Ordinary 1 11;
    def_math_char "\\beta" Box.Ordinary 1 12;
    def_math_char "\\gamma" Box.Ordinary 1 13;
    def_math_char "\\delta" Box.Ordinary 1 14;
    def_math_char "\\epsilon" Box.Ordinary 1 15;
    def_math_char "\\zeta" Box.Ordinary 1 16;
    def_math_char "\\eta" Box.Ordinary 1 17;
    def_math_char "\\theta" Box.Ordinary 1 18;
    def_math_char "\\iota" Box.Ordinary 1 19;
    def_math_char "\\kappa" Box.Ordinary 1 20;
    def_math_char "\\lambda" Box.Ordinary 1 21;
    def_math_char "\\mu" Box.Ordinary 1 22;
    def_math_char "\\nu" Box.Ordinary 1 23;
    def_math_char "\\xi" Box.Ordinary 1 24;
    def_math_char "\\pi" Box.Ordinary 1 25;
    def_math_char "\\rho" Box.Ordinary 1 26;
    def_math_char "\\sigma" Box.Ordinary 1 27;
    def_math_char "\\tau" Box.Ordinary 1 28;
    def_math_char "\\upsilon" Box.Ordinary 1 29;
    def_math_char "\\phi" Box.Ordinary 1 30;
    def_math_char "\\chi" Box.Ordinary 1 31;
    def_math_char "\\psi" Box.Ordinary 1 32;
    def_math_char "\\omega" Box.Ordinary 1 33;
    def_math_char "\\varepsilon" Box.Ordinary 1 34;
    def_math_char "\\vartheta" Box.Ordinary 1 35;
    def_math_char "\\varpi" Box.Ordinary 1 36;
    def_math_char "\\varrho" Box.Ordinary 1 37;
    def_math_char "\\varsigma" Box.Ordinary 1 38;
    def_math_char "\\varphi" Box.Ordinary 1 39;
    def_math_char "\\Gamma" Box.Ordinary 0 0;
    def_math_char "\\Delta" Box.Ordinary 0 1;
    def_math_char "\\Theta" Box.Ordinary 0 2;
    def_math_char "\\Lambda" Box.Ordinary 0 3;
    def_math_char "\\Xi" Box.Ordinary 0 4;
    def_math_char "\\Pi" Box.Ordinary 0 5;
    def_math_char "\\Sigma" Box.Ordinary 0 6;
    def_math_char "\\Upsilon" Box.Ordinary 0 7;
    def_math_char "\\Phi" Box.Ordinary 0 8;
    def_math_char "\\Psi" Box.Ordinary 0 9;
    def_math_char "\\Omega" Box.Ordinary 0 10;
    def_math_char "\\aleph" Box.Ordinary 2 64;
    def_math_char "\\imath" Box.Ordinary 1 123;
    def_math_char "\\jmath" Box.Ordinary 1 124;
    def_math_char "\\ell" Box.Ordinary 1 96;
    def_math_char "\\wp" Box.Ordinary 1 125;
    def_math_char "\\Re" Box.Ordinary 2 60;
    def_math_char "\\Im" Box.Ordinary 2 61;
    def_math_char "\\partial" Box.Ordinary 1 64;
    def_math_char "\\infty" Box.Ordinary 2 49;
    def_math_char "\\prime" Box.Ordinary 2 48;
    def_math_char "\\emptyset" Box.Ordinary 2 59;
    def_math_char "\\nabla" Box.Ordinary 2 114;
    def_math_char "\\surd" Box.Operator 2 112;
    def_math_char "\\top" Box.Ordinary 2 62;
    def_math_char "\\bot" Box.Ordinary 2 63;
    def_math_char "\\triangle" Box.Ordinary 2 52;
    def_math_char "\\forall" Box.Ordinary 2 56;
    def_math_char "\\exists" Box.Ordinary 2 57;
    def_math_char "\\neg" Box.Ordinary 2 58;
    def_math_char "\\flat" Box.Ordinary 1 91;
    def_math_char "\\natural" Box.Ordinary 1 92;
    def_math_char "\\sharp" Box.Ordinary 1 93;
    def_math_char "\\clubsuit" Box.Ordinary 2 124;
    def_math_char "\\diamondsuit" Box.Ordinary 2 125;
    def_math_char "\\heartsuit" Box.Ordinary 2 126;
    def_math_char "\\spadesuit" Box.Ordinary 2 127;
    def_math_char "\\coprod" Box.Operator 3 96;
    def_math_char "\\bigvee" Box.Operator 3 87;
    def_math_char "\\bigwedge" Box.Operator 3 86;
    def_math_char "\\biguplus" Box.Operator 3 85;
    def_math_char "\\bigcap" Box.Operator 3 84;
    def_math_char "\\bigcup" Box.Operator 3 83;
    def_math_char "\\intop" Box.Operator 3 82;
    def_math_char "\\prod" Box.Operator 3 81;
    def_math_char "\\sum" Box.Operator 3 80;
    def_math_char "\\bigotimes" Box.Operator 3 78;
    def_math_char "\\bigoplus" Box.Operator 3 76;
    def_math_char "\\bigodot" Box.Operator 3 74;
    def_math_char "\\ointop" Box.Operator 3 72;
    def_math_char "\\bigsqcup" Box.Operator 3 70;
    def_math_char "\\smallint" Box.Operator 2 115;
    def_math_char "\\triangleleft" Box.BinOp 1 47;
    def_math_char "\\triangleright" Box.BinOp 1 46;
    def_math_char "\\bigtriangleup" Box.BinOp 2 52;
    def_math_char "\\bigtriangledown" Box.BinOp 2 53;
    def_math_char "\\wedge" Box.BinOp 2 94;
    def_math_char "\\vee" Box.BinOp 2 95;
    def_math_char "\\cap" Box.BinOp 2 92;
    def_math_char "\\cup" Box.BinOp 2 91;
    def_math_char "\\ddagger" Box.BinOp 2 122;
    def_math_char "\\dagger" Box.BinOp 2 121;
    def_math_char "\\sqcap" Box.BinOp 2 117;
    def_math_char "\\sqcup" Box.BinOp 2 116;
    def_math_char "\\uplus" Box.BinOp 2 93;
    def_math_char "\\amalg" Box.BinOp 2 113;
    def_math_char "\\diamond" Box.BinOp 2 5;
    def_math_char "\\bullet" Box.BinOp 2 15;
    def_math_char "\\wr" Box.BinOp 2 111;
    def_math_char "\\div" Box.BinOp 2 4;
    def_math_char "\\odot" Box.BinOp 2 12;
    def_math_char "\\oslash" Box.BinOp 2 11;
    def_math_char "\\otimes" Box.BinOp 2 10;
    def_math_char "\\ominus" Box.BinOp 2 9;
    def_math_char "\\oplus" Box.BinOp 2 8;
    def_math_char "\\mp" Box.BinOp 2 7;
    def_math_char "\\pm" Box.BinOp 2 6;
    def_math_char "\\circ" Box.BinOp 2 14;
    def_math_char "\\bigcirc" Box.BinOp 2 13;
    def_math_char "\\setminus" Box.BinOp 2 110;
    def_math_char "\\cdot" Box.BinOp 2 1;
    def_math_char "\\ast" Box.BinOp 2 3;
    def_math_char "\\times" Box.BinOp 2 2;
    def_math_char "\\star" Box.BinOp 1 63;
    def_math_char "\\propto" Box.Relation 2 47;
    def_math_char "\\sqsubseteq" Box.Relation 2 118;
    def_math_char "\\sqsupseteq" Box.Relation 2 119;
    def_math_char "\\parallel" Box.Relation 2 107;
    def_math_char "\\mid" Box.Relation 2 106;
    def_math_char "\\mvert" Box.BinOp 2 106;
    def_math_char "\\dashv" Box.Relation 2 97;
    def_math_char "\\vdash" Box.Relation 2 96;
    def_math_char "\\nearrow" Box.Relation 2 37;
    def_math_char "\\searrow" Box.Relation 2 38;
    def_math_char "\\nwarrow" Box.Relation 2 45;
    def_math_char "\\swarrow" Box.Relation 2 46;
    def_math_char "\\Leftrightarrow" Box.Relation 2 44;
    def_math_char "\\Leftarrow" Box.Relation 2 40;
    def_math_char "\\Rightarrow" Box.Relation 2 41;
    def_math_char "\\leq" Box.Relation 2 20;
    def_math_char "\\geq" Box.Relation 2 21;
    def_math_char "\\succ" Box.Relation 2 31;
    def_math_char "\\prec" Box.Relation 2 30;
    def_math_char "\\approx" Box.Relation 2 25;
    def_math_char "\\succeq" Box.Relation 2 23;
    def_math_char "\\preceq" Box.Relation 2 22;
    def_math_char "\\supset" Box.Relation 2 27;
    def_math_char "\\subset" Box.Relation 2 26;
    def_math_char "\\supseteq" Box.Relation 2 19;
    def_math_char "\\subseteq" Box.Relation 2 18;
    def_math_char "\\in" Box.Relation 2 50;
    def_math_char "\\ni" Box.Relation 2 51;
    def_math_char "\\gg" Box.Relation 2 29;
    def_math_char "\\ll" Box.Relation 2 28;
    def_math_char "\\not" Box.Relation 2 54;
    def_math_char "\\leftrightarrow" Box.Relation 2 36;
    def_math_char "\\leftarrow" Box.Relation 2 32;
    def_math_char "\\rightarrow" Box.Relation 2 33;
    def_math_char "\\mapstochar" Box.Relation 2 55;
    def_math_char "\\sim" Box.Relation 2 24;
    def_math_char "\\simeq" Box.Relation 2 39;
    def_math_char "\\perp" Box.Relation 2 63;
    def_math_char "\\equiv" Box.Relation 2 17;
    def_math_char "\\asymp" Box.Relation 2 16;
    def_math_char "\\smile" Box.Relation 1 94;
    def_math_char "\\frown" Box.Relation 1 95;
    def_math_char "\\leftharpoonup" Box.Relation 1 40;
    def_math_char "\\leftharpoondown" Box.Relation 1 41;
    def_math_char "\\rightharpoonup" Box.Relation 1 42;
    def_math_char "\\rightharpoondown" Box.Relation 1 43;
    def_math_char "\\lhook" Box.Relation 1 44;
    def_math_char "\\rhook" Box.Relation 1 45;
    def_math_char "\\ldotp" Box.Punct 1 58;
    def_math_char "\\cdotp" Box.Punct 2 1;
    def_math_char "\\colon" Box.Punct 0 58;
    def_math_delim "\\lmoustache" Box.Open 3 122 3 64;
    def_math_delim "\\rmoustache" Box.Close 3 123 3 65;
    def_math_delim "\\arrowvert" Box.Ordinary 2 106 3 60;
    def_math_delim "\\Arrowvert" Box.Ordinary 2 197 3 61;
    def_math_delim "\\vert" Box.Ordinary 2 106 3 12;
    def_math_delim "\\rvert" Box.Close 2 106 3 12;
    def_math_delim "\\lvert" Box.Open 2 106 3 12;
    def_math_delim "\\mvert" Box.Relation 2 106 3 12;
    def_math_delim "\\Vert" Box.Ordinary 2 107 3 13;
    def_math_delim "\\uparrow" Box.Relation 2 34 3 120;
    def_math_delim "\\downarrow" Box.Relation 2 35 3 121;
    def_math_delim "\\updownarrow" Box.Relation 2 108 3 63;
    def_math_delim "\\Uparrow" Box.Relation 2 42 3 126;
    def_math_delim "\\Downarrow" Box.Relation 2 43 3 127;
    def_math_delim "\\Updownarrow" Box.Relation 2 109 3 119;
    def_math_delim "\\backslash" Box.Ordinary 2 110 3 15;
    def_math_delim "\\rangle" Box.Close 2 105 3 11;
    def_math_delim "\\langle" Box.Open 2 104 3 10;
    def_math_delim "\\rbrace" Box.Close 2 103 3 9;
    def_math_delim "\\lbrace" Box.Open 2 102 3 8;
    def_math_delim "\\rceil" Box.Close 2 101 3 7;
    def_math_delim "\\lceil" Box.Open 2 100 3 6;
    def_math_delim "\\rfloor" Box.Close 2 99 3 5;
    def_math_delim "\\lfloor" Box.Open 2 98 3 4;
    def_math_acc "\\acute" 0 19;
    def_math_acc "\\grave" 0 18;
    def_math_acc "\\ddot" 0 127;
    def_math_acc "\\tilde" 0 126;
    def_math_acc "\\bar" 0 22;
    def_math_acc "\\breve" 0 21;
    def_math_acc "\\check" 0 20;
    def_math_acc "\\hat" 0 94;
    def_math_acc "\\vec" 1 126;
    def_math_acc "\\dot" 0 95;
    def_math_acc "\\widetilde" 3 101;
    def_math_acc "\\widehat" 3 98;
    set_math_code_table ps default_math_code_table

(*-----------------------------------------------------------------------*)

let py_initialize_parse_state =
  python_interfaced_function
    [| CamlpillSubtype "parse_state" |]
    begin
      fun py_args ->
        let ps = unpythonize_parse_state py_args.(0) in
          initialise ps;
          pynone ();
    end

let _ =
  register_for_python
    [|
      ("initialize_parse_state", py_initialize_parse_state);
    |]

(*-----------------------------------------------------------------------*)
